*Юрий Дудь признан иноагентом
В этом проекте происходит исследование данных о выпусках одного из самых известных русскоязычных youtube*-каналов с интервью. Аудитория этого канала на текущий момент составляет более 10 млн человек.
Предыстория: в книге Александра Богачева "Графики, которые убеждают всех" я увидела анализ данных канала Юрия Дудя* в качестве примера, как можно подходить к разбору темы с разных сторон (А.Богачев "Графики, которые убеждают всех", Издательство АСТ, 2020; стр.58-72). В книге разобраны данные о выпусках 2017 года. С тех пор прошло уже более 5 лет, и мне стало интересно узнать, что изменилось на канале за это время.
Основные задачи:
План работы:
Описание данных:
Данные о просмотрах, лайках, комментариях, продолжительности и названии видео собраны с платформы youtube при помощи API. Для этого понадобилось зарегистрироваться на платформе в разделе google developers и получить там API-ключ. Далее в jupyter notebook были загружены необходимые бибилиотеки, получены и обработаны данные, сформированы датафреймы.
Дата сбора данных: 18 ноября 2022 года
Данных о профессиях и именах героев каждого видео за первые 4 года были получены отсюда: https://stevsky.ru/interesno/uriy-dud-v-tsifrach-4-goda-statistiki (но были внесены некоторые правки)
Остальные данные об именах и профессиях героев, их дате и месте рождения добавлены из Википедии*-свободной энциклопедии.
Роскомнадзор: сайт нарушает закон РФ Минюст признал иноагентом
Роскомнадзор: сайт нарушает закон РФ Роскомнадзор: сайт нарушает закон РФ
P.S. Удивительно, как быстро сжалось пространство свободы: ещё 2 года назад в книге "Графики, которые убеждают всех" автору не нужно было ставить никому ни одной маркировки. Теперь же - прошу прощения за постоянное "спотыкание" при чтении, необходимые сноски сделаны ради безопасности.
Импортируем необходимые библиотеки:
import pandas as pd
import os
import numpy as np
from datetime import datetime, date, timedelta
import textwrap
from matplotlib import pyplot as plt
import seaborn as sns
Для того, чтобы было удобно рассматривать большие числа, внесём некоторые изменения:
class _IntArrayFormatter(pd.io.formats.format.GenericArrayFormatter):
def _format_strings(self):
formatter = self.formatter or (lambda x: ' {:,}'.format(x).replace(',', ' '))
fmt_values = [formatter(x) for x in self.values]
return fmt_values
pd.io.formats.format.IntArrayFormatter = _IntArrayFormatter
Загружаем датафрейм с общей информацией о канале:
if os.path.exists('/datasets/vdud/vdud_main.xlsx'):
vdud_main = pd.read_excel('/datasets/vdud/vdud_main.xlsx')
else:
vdud_main = pd.read_excel('/C:/datasets/vdud/vdud_main.xlsx')
vdud_main = vdud_main.drop(['id', 'published_at'], axis=1)
vdud_main
| title | description | view_count | subscriber_count | video_count | |
|---|---|---|---|---|---|
| 0 | вДудь | Здесь задают вопросы | 1 858 295 090 | 10 100 000 | 159 |
Загружаем датафрейм с подробными данными обо всех видео канала:
if os.path.exists('/datasets/vdud/vdud_table_full.xlsx'):
df_row = pd.read_excel('/datasets/vdud/vdud_table_full.xlsx')
else:
df_row = pd.read_excel('/C:/datasets/vdud/vdud_table_full.xlsx')
Создадим второй датафрейм, который будем дополнять и преобразовывать. В дальнейшем его можно будет сравнить с изначальными данными (например, по количеству столбцов).
if os.path.exists('/datasets/vdud/vdud_table_full.xlsx'):
df = pd.read_excel('/datasets/vdud/vdud_table_full.xlsx')
else:
df = pd.read_excel('/C:/datasets/vdud/vdud_table_full.xlsx')
Применяем собственную функцию для ознакомления с данными:
def my_check_function (dataset):
'''Функция для ознакомления и проверки даных'''
print('Как выглядят случайные 5 строк:')
display(dataset.sample(5, random_state=42))
print()
print('Общая информация о данных, наименования столбцов, типы данных:')
print(dataset.info())
print()
print('Есть ли пропуски, сколько их:')
print(dataset.isna().sum())
print('Доля пропущенных значений:')
print(dataset.isna().mean())
print()
print('Есть ли явные дубликаты, сколько их:')
print(dataset.duplicated().sum())
print('Доля явных дубликатов:')
print(dataset.duplicated().mean())
print()
print('Подробное описание данных:')
display(dataset.describe())
my_check_function(df)
Как выглядят случайные 5 строк:
| video_id | video_title | guest_name | guest_birthday | guest_birthplace | birthplace_region | birthplace_country | guest_type | video_type | upload_date | duration | view_count | like_count | comment_count | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 78 | kiu2CSB1JMw | Идов - чекисты, Монеточка, застой (English subs) | Михаил Идов | 1976-07-09 | Рига | NaN | Латвия | Режиссёр | Интервью | 2019-02-26 | 01:10:44 | 4 731 194 | 116 399 | 15 267 |
| 155 | Q0oRii7zV9A | Маша Гессен – стыдные вопросы про Америку / Ma... | Маша Гессен | 1967-01-13 | Москва | Москва | Россия | Журналист | Интервью | 2022-09-20 | 01:55:26 | 5 347 391 | 148 992 | 24 492 |
| 128 | skLvb0JCNJk | Ирина Кайратовна – новые звезды из Казахстана ... | творч. объединение "Ирина Кайратовна" | NaT | Казахстан | NaN | Казахстан | Музыкант | Интервью | 2021-05-13 | 01:54:34 | 11 165 120 | 340 385 | 50 161 |
| 55 | 2kJoQS5m2WU | Балабанов - гениальный русский режиссер / вДудь | Алексей Балабанов | 1959-02-25 | Екатеринбург | Свердловская область | Россия | Режиссёр | Фильм | 2018-05-15 | 02:10:06 | 12 134 261 | 233 184 | 16 824 |
| 94 | zl7FAusDuAY | Бекмамбетов - реклама в «Елках», BadComedian, ... | Тимур Бекмамбетов | 1961-06-25 | Атырау | NaN | Казахстан | Режиссёр | Интервью | 2019-10-31 | 01:27:30 | 6 094 334 | 124 179 | 34 581 |
Общая информация о данных, наименования столбцов, типы данных: <class 'pandas.core.frame.DataFrame'> RangeIndex: 159 entries, 0 to 158 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 video_id 159 non-null object 1 video_title 159 non-null object 2 guest_name 159 non-null object 3 guest_birthday 131 non-null datetime64[ns] 4 guest_birthplace 133 non-null object 5 birthplace_region 103 non-null object 6 birthplace_country 133 non-null object 7 guest_type 159 non-null object 8 video_type 159 non-null object 9 upload_date 159 non-null object 10 duration 159 non-null object 11 view_count 159 non-null int64 12 like_count 159 non-null int64 13 comment_count 159 non-null int64 dtypes: datetime64[ns](1), int64(3), object(10) memory usage: 17.5+ KB None Есть ли пропуски, сколько их: video_id 0 video_title 0 guest_name 0 guest_birthday 28 guest_birthplace 26 birthplace_region 56 birthplace_country 26 guest_type 0 video_type 0 upload_date 0 duration 0 view_count 0 like_count 0 comment_count 0 dtype: int64 Доля пропущенных значений: video_id 0.000000 video_title 0.000000 guest_name 0.000000 guest_birthday 0.176101 guest_birthplace 0.163522 birthplace_region 0.352201 birthplace_country 0.163522 guest_type 0.000000 video_type 0.000000 upload_date 0.000000 duration 0.000000 view_count 0.000000 like_count 0.000000 comment_count 0.000000 dtype: float64 Есть ли явные дубликаты, сколько их: 0 Доля явных дубликатов: 0.0 Подробное описание данных:
| view_count | like_count | comment_count | |
|---|---|---|---|
| count | 1.590000e+02 | 1.590000e+02 | 159.000000 |
| mean | 1.168778e+07 | 3.259258e+05 | 40668.075472 |
| std | 7.722975e+06 | 2.388079e+05 | 34404.208341 |
| min | 3.027571e+06 | 7.447000e+04 | 5508.000000 |
| 25% | 6.398358e+06 | 1.733740e+05 | 19375.000000 |
| 50% | 9.221750e+06 | 2.462290e+05 | 30274.000000 |
| 75% | 1.373760e+07 | 3.753705e+05 | 50351.000000 |
| max | 4.898500e+07 | 1.278548e+06 | 223917.000000 |
Вывод:
Приводим данные к форматам, которые понадобятся для удобства дальнейшего исследования:
df['guest_birthday'] = pd.to_datetime(df['guest_birthday'], format='%Y-%m-%d')
df['upload_date'] = pd.to_datetime(df['upload_date'], format='%Y-%m-%d')
df['duration'] = pd.to_datetime(df['duration'],format='%H:%M:%S').dt.time
Заполним пропуски:
df['guest_birthplace'] = df['guest_birthplace'].fillna('неизвестно')
df['birthplace_region'] = df['birthplace_region'].fillna('неизвестно')
df['birthplace_country'] = df['birthplace_country'].fillna('неизвестно')
Выделим возраст гостя на момент выхода интервью с ним:
df['guest_age_on_video'] = ((df['upload_date'] - df['guest_birthday']) /365).astype(str).str[:2]
df['guest_age_on_video'] = pd.to_numeric(df['guest_age_on_video'], errors='coerce')
Выделим условные поколения. Поскольку у теории поколений есть разные версии, выберем ту, что подходит к нашему исследованию больше всего: https://ru.wikipedia.org/wiki/Теория_поколений#Мини-поколения_в_России_(1902-2001) (раздел 6.Теория поколений в Постсоветском пространстве)Роскомнадзор: сайт нарушает закон РФ
Для создания столбца со значениями поколений выделим отдельно год рождения героев:
df['guest_birth_year'] = pd.to_datetime(df['guest_birthday'], errors='coerce').dt.year
df['guest_birth_year'] = pd.to_numeric(df['guest_birth_year'], errors='coerce')
Создадим функцию для определения категорий поколений:
def generation_category (guest_birth_year):
"""
Возвращает наименование условного поколения в зависимости от года рождения героя
"""
if guest_birth_year < 1943:
return 'Молчаливое поколение 1928-1942'
if guest_birth_year < 1963:
return 'Бэби-бумер 1943-1962'
if guest_birth_year < 1982:
return 'Поколение X 1963-1981'
if guest_birth_year < 2001:
return 'Поколение Миллениума 1982-2000'
if guest_birth_year < 2017:
return 'Хоумлендер (домосед) 2001-2016'
else:
return 'поколение неизвестно'
Применяем функцию, создаём новый столбец:
df['guest_generation'] = df['guest_birth_year'].apply(generation_category)
Также, согласно действующему законодательству: http://duma.gov.ru/news/50394/, можно выделить "молодёжь" - людей в возрасте от 14 до 35 лет. Выделим гостей, которые на момент выхода видео считались молодёжью с точки зрения закона, а кто нет. Создадим функцию:
def find_youth(guest_age_on_video):
"""
Возвращает "да", если гость на момент выхода видео относился к категории "молодёжь" с точки зрения закона
и "нет", если не относился
"""
if guest_age_on_video < 36:
return 'yes'
if guest_age_on_video >=36:
return 'no'
else:
return 'other'
df['is_guest_youth'] = df['guest_age_on_video'].apply(find_youth)
Теперь дополним имеющиеся данные о месте рождения героев. У нас имеются названия регионов России. Мы можем добавить сведения о том, к каким федеральным округам относятся эти регионы. Для этого нам понадобятся новые внешние данные:
if os.path.exists('/datasets/vdud/regions.csv'):
regions = pd.read_csv('/datasets/vdud/regions.csv', sep=';')
else:
regions = pd.read_csv('/C:/datasets/vdud/regions.csv', sep=';')
Данные получены отсюда: https://mydata.biz/ru/catalog/databases/oktmo
regions = regions.drop(regions.columns[[0, 1,3]], axis=1)
regions.columns = ('birthplace_region', 'federal_district')
regions
| birthplace_region | federal_district | |
|---|---|---|
| 0 | Белгородская область | Центральный федеральный округ |
| 1 | Брянская область | Центральный федеральный округ |
| 2 | Владимирская область | Центральный федеральный округ |
| 3 | Воронежская область | Центральный федеральный округ |
| 4 | Ивановская область | Центральный федеральный округ |
| ... | ... | ... |
| 81 | Сахалинская область | Дальневосточный федеральный округ |
| 82 | Еврейская автономная область | Дальневосточный федеральный округ |
| 83 | Чукотский автономный округ | Дальневосточный федеральный округ |
| 84 | Республика Крым | Южный федеральный округ |
| 85 | город Севастополь | Южный федеральный округ |
86 rows × 2 columns
Присоединяем таблицу с федеральными округами:
df = df.merge(regions, on='birthplace_region', how='left')
Заполняем пропуски:
df['federal_district'] = df['federal_district'].fillna('неизвестно')
Проверяем:
df['federal_district'].value_counts()
неизвестно 112 Центральный федеральный округ 14 Уральский федеральный округ 9 Сибирский федеральный округ 8 Южный федеральный округ 5 Приволжский федеральный округ 4 Дальневосточный федеральный округ 3 Северо-Западный федеральный округ 3 Северо–Кавказский федеральный округ 1 Name: federal_district, dtype: int64
Подозрительно много данных в варианте "неизвестно", проверим, что внутри:
df.query('federal_district == "неизвестно"')['birthplace_region'].value_counts()
неизвестно 56 Москва 33 Санкт-Петербург 14 Башкортостан 4 Читинская область 1 Ленинрадская область 1 Кабардино-Балкария 1 Северная Осетия - Алания 1 республика Саха (Якутия) 1 Name: birthplace_region, dtype: int64
Есть проблема: некоторые места, видимо, записаны по-разному в двух таблицах, поэтому они не соединились правильно. Посмотрим, какие у нас варианты в таблице regions:
regions['birthplace_region'].unique()
array(['Белгородская область', 'Брянская область', 'Владимирская область',
'Воронежская область', 'Ивановская область', 'Калужская область',
'Костромская область', 'Курская область', 'Липецкая область',
'Московская область', 'Орловская область', 'Рязанская область',
'Смоленская область', 'Тамбовская область', 'Тверская область',
'Тульская область', 'Ярославская область', 'город Москва',
'Республика Карелия', 'Республика Коми', 'Архангельская область',
'Вологодская область', 'Калининградская область',
'Ленинградская область', 'Мурманская область',
'Новгородская область', 'Псковская область',
'город Санкт-Петербург', 'Ненецкий автономный округ',
'Республика Адыгея', 'Республика Калмыкия', 'Краснодарский край',
'Астраханская область', 'Волгоградская область',
'Ростовская область', 'Республика Дагестан',
'Республика Ингушетии', 'Кабардино-Балкарская Республика',
'Карачаево-Черкесская Республика',
'Республика Северная Осетия-Алания', 'Чеченская Республика',
'Ставропольский край', 'Республика Башкортостан',
'Республика Марий Эл', 'Республика Мордовии',
'Республика Татарстан', 'Удмуртская Республики',
'Чувашская Республики', 'Пермский край', 'Кировская область',
'Нижегородская область', 'Оренбургская область',
'Пензенская область', 'Самарская область', 'Саратовская область',
'Ульяновская область', 'Курганская область',
'Свердловская область', 'Тюменская область', 'Челябинская область',
'Ханты-Мансийский автономный округ - Югра',
'Ямало-Ненецкий автономный округ', 'Республика Алтай',
'Республика Бурятия', 'Республика Тыва', 'Республика Хакасия',
'Алтайский край', 'Забайкальский край', 'Красноярский край',
'Иркутская область', 'Кемеровская область',
'Новосибирская область', 'Омская область', 'Томская область',
'Республика Саха (Якутия)', 'Камчатский край', 'Приморский край',
'Хабаровский край', 'Амурская область', 'Магаданская область',
'Сахалинская область', 'Еврейская автономная область',
'Чукотский автономный округ', 'Республика Крым',
'город Севастополь'], dtype=object)
Вносим правки в нашу основную таблицу, чтобы все регионы назывались правильно:
df.loc[df['birthplace_region']=='Москва', 'birthplace_region'] = 'город Москва'
df.loc[df['birthplace_region']=='Санкт-Петербург', 'birthplace_region'] = 'город Санкт-Петербург'
df.loc[df['birthplace_region']=='Башкортостан', 'birthplace_region'] = 'Республика Башкортостан'
df.loc[df['birthplace_region']=='Читинская область', 'birthplace_region'] = 'Забайкальский край'
df.loc[df['birthplace_region']=='Ленинрадская область', 'birthplace_region'] = 'Ленинградская область'
df.loc[df['birthplace_region']=='Кабардино-Балкария', 'birthplace_region'] = 'Кабардино-Балкарская Республика'
df.loc[df['birthplace_region']=='Северная Осетия - Алания', 'birthplace_region'] ='Республика Северная Осетия-Алания'
df.loc[df['birthplace_region']=='республика Саха (Якутия)', 'birthplace_region'] = 'Республика Саха (Якутия)'
Убираем столбец, который присоединился неправильно:
df = df.drop(columns=['federal_district'])
И присоединям заново. Теперь должно быть без ошибок:
df = df.merge(regions, on='birthplace_region', how='left')
df['federal_district'] = df['federal_district'].fillna('неизвестно')
Проверяем: количество строк "неизвестно" в федеральных округах должно быть такое же, как и в названиях регионов:
len(df.loc[df['federal_district']=='неизвестно'])
56
len(df.loc[df['birthplace_region']=='неизвестно'])
56
Мы убедились: теперь всё присоединено без ошибок.
Также добавим разделение на категории: выделим отдельно Москву, Санкт-Петербург и регионы России. Создадим функцию:
def find_region(birthplace_region):
"""
Выводим отдельно Москву, Санкт-Петербург и регионы РФ
"""
for region in birthplace_region:
if birthplace_region == 'город Москва':
return 'Москва'
if birthplace_region == 'город Санкт-Петербург':
return 'Санкт-Петербург'
if birthplace_region == 'неизвестно':
return 'неизвестно'
else:
return 'регионы РФ'
И применим её:
birthplace_region = df['birthplace_region']
df['region'] = df['birthplace_region'].apply(find_region)
Выделяем составные части дат выпусков (год, месяц, неделю, день месяца, название дня недели):
df['upload_year'] = pd.to_datetime(df['upload_date']).dt.year
df['upload_month'] = pd.to_datetime(df['upload_date']).dt.month
# сделаем дополнительно столбец с названием месяца, может понадобиться для визуализации
df['upload_month_name'] = pd.to_datetime(df['upload_date']).dt.strftime('%B')
df['upload_week'] = pd.to_datetime(df['upload_date']).dt.isocalendar().week
df['upload_day'] = pd.to_datetime(df['upload_date']).dt.day
df['upload_weekday'] = pd.to_datetime(df['upload_date']).dt.strftime('%A')
Добавим столбец с продолжительностью видео в минутах (для удобства дальнейших расчётов):
df['duration_minutes'] = (df['duration'].astype(str).str.split(':')
.apply(lambda x: int(x[0]) * 60 + int(x[1])))
Большинство гостей канала - мужчины. Но попробуем выделить и другие варианты (например, женщины, смешанные группы):
woman_names = ['Ксения', 'Настя', 'Надежда', 'Ирина Горбачёва', 'Елизавета', 'Александра', 'Катерина',
'Тамара', 'Мария', 'Маша']
mixed_group_names = ['Молодые артисты', 'MTV', 'группа IC3PEAK', ' и ', 'белорусы', 'украинцы, русские',
'Борзунова, Шепелин, Пикули']
no_gender_names = ['Рок музыка', 'Колыма', 'Замбия', 'Беслан', 'ВИЧ', 'Камчатка', 'Фильм']
def find_gender(guest_name):
for name in woman_names:
if name in guest_name:
return 'woman'
for name in mixed_group_names:
if name in guest_name:
return 'mixed_group'
for name in no_gender_names:
if name in guest_name:
return 'no_gender'
for name in guest_name:
return 'man'
Применяем функцию и добавляем новый столбец:
df['gender'] = df['guest_name'].apply(find_gender)
Выделим информацию о субтитрах на английском языке:
def find_subt(video_title):
for title in video_title:
if 'nglish sub' in video_title:
return 'yes'
else:
return 'no'
df['english_subtitles'] = df['video_title'].apply(find_subt)
Названия выпусков отличаются по длине: есть и лаконичные, и длинные названия. Это может помешать нам при визуализации. Создадим короткую версию названий видео в отдельном столбце. Для этого напишем функцию:
def short_title(video_title):
for title in video_title:
result=textwrap.shorten(video_title, width=40, placeholder='...')
return result
И вставим новый столбец рядом с полным названием видео в начало датафрейма:
df.insert(2, 'short_video_title', df['video_title'].apply(short_title))
Также для удобства восприятия дальнейшей визуализации создадим столбец, где просмотры будут указаны в миллионах:
df['view_count_mln'] = df['view_count'] / 1000000
Создадим более широкую группировку гостей по профессиям:
journalist_names = ['Журналист','Телеведущий','Телеканал','СМИ']
artist_names = ['Артисты','Художник','Мультипликатор','Активист']
scientists_names = ['Историк','Экономист','Астрофизик']
society_names = ['Россия','Человек','Замбия','Беларусь']
IT_names = ['IT', 'Дизайнер']
def find_profi(guest_type):
for name in journalist_names:
if name in guest_type:
return 'Журналистика/СМИ'
for name in artist_names:
if name in guest_type:
return 'Артисты/художники'
for name in scientists_names:
if name in guest_type:
return 'Учёные'
for name in society_names:
if name in guest_type:
return 'Человек и общество'
for name in IT_names:
if name in guest_type:
return 'IT-шники'
if guest_type == 'Музыкант':
return 'Музыка'
elif guest_type == 'Актёр':
return 'Театр/кино (артисты)'
elif guest_type == 'Режиссёр':
return 'Театр/кино (режиссёры)'
elif guest_type == 'Комик':
return 'Юмор'
elif guest_type == 'Блогер':
return 'Блогинг'
elif guest_type == 'Политик':
return 'Политика'
elif guest_type == 'Бизнесмен':
return 'Бизнес'
elif guest_type == 'Продюсер':
return 'Продюсеры'
elif guest_type == 'Писатель':
return 'Писатели'
else:
return 'неизвестно'
df['guest_profi'] = df['guest_type'].apply(find_profi)
Определим выпуски, в которых гость - тот человек, который уже однажды был на интервью на этом канале. Создаём список из имён таких гостей:
df_double = df.loc[df.duplicated(['guest_name'], keep=False)]
# есть гость, который был на интервью сначала один, потом с женой, поэтому графа 'guest_name' отличается
# найдём его по дню рождения
df_double = df_double.append(df.loc[df['guest_birthday']=='1976-06-04']).sort_values(by='guest_name')
double_names = df_double['guest_name'].unique()
Создаём столбец, где будет указано, дублируется этот гость или нет:
df['double'] = df['guest_name'].apply(lambda x: 'double' if x in double_names else 'no_double')
Отношение аудитории к выпуску можно оценить не только с помощью имеющихся чисел (количество просмотров, лайков, комментариев), но и с помощью отношения этих показателей друг к другу.
С помощью новых расчётов мы сможем выделить:
Это наиболее очевидные метрики. Но кроме них, мы попробуем замерить негативную дискуссионную реакцию:
Почему мы будем замерять "негатив" таким образом?
Мы находимся в ситуации, когда у нас нет доступа к информации о дизлайках (счётчик дизлайков скрыт, о мотивах принятия такого решения можно прочитать здесь: https://support.google.com/youtube/thread/134803817/Новости-об-отметках-Не-нравится?hl=ru&msgid=134803817). Поэтому мы можем по косвенным проявлениям попытаться увидеть негативную реакцию аудитории.Что может сделать пользователь, если он недоволен? Конечно, он может просто не смотреть или не досмотреть выпуск. Но он также может оставить свой негативный комментарий. Если комментариев по отношению к лайкам становится больше, возможно, это повод насторожиться: в комментариях может быть не совсем дружественная дискуссия. Это мы и попытаемся замерить в этой метрике.
Добавим новые данные в датафрейм:
df['like_perc'] = df['like_count'] / df['view_count'] * 100
df['comment_perc'] = df['comment_count'] / df['view_count'] * 100
df['comment_like_perc'] = df['comment_count'] / df['like_count'] * 100
У нас получилось много столбцов, распределим их положение внутри датафрейма согласно логике их содержания. Посмотрим, в каком порядке расположены столбцы сейчас:
cols=df.columns.tolist()
df_cols = pd.DataFrame(cols)
df_cols
| 0 | |
|---|---|
| 0 | video_id |
| 1 | video_title |
| 2 | short_video_title |
| 3 | guest_name |
| 4 | guest_birthday |
| 5 | guest_birthplace |
| 6 | birthplace_region |
| 7 | birthplace_country |
| 8 | guest_type |
| 9 | video_type |
| 10 | upload_date |
| 11 | duration |
| 12 | view_count |
| 13 | like_count |
| 14 | comment_count |
| 15 | guest_age_on_video |
| 16 | guest_birth_year |
| 17 | guest_generation |
| 18 | is_guest_youth |
| 19 | federal_district |
| 20 | region |
| 21 | upload_year |
| 22 | upload_month |
| 23 | upload_month_name |
| 24 | upload_week |
| 25 | upload_day |
| 26 | upload_weekday |
| 27 | duration_minutes |
| 28 | gender |
| 29 | english_subtitles |
| 30 | view_count_mln |
| 31 | guest_profi |
| 32 | double |
| 33 | like_perc |
| 34 | comment_perc |
| 35 | comment_like_perc |
Определим новый порядок расположения столбцов:
df = df[df.columns[[0, 1, 2, 3, 28, 32, 4, 15, 16, 17, 18, 5, 6, 20, 19, 7, 8, 31, 9, 10,
21, 22, 23, 24, 25, 26, 11, 27, 29, 12, 30, 13, 14, 33, 34, 35]]]
Проверяем новый порядок:
cols=df.columns.tolist()
df_cols = pd.DataFrame(cols)
df_cols
| 0 | |
|---|---|
| 0 | video_id |
| 1 | video_title |
| 2 | short_video_title |
| 3 | guest_name |
| 4 | gender |
| 5 | double |
| 6 | guest_birthday |
| 7 | guest_age_on_video |
| 8 | guest_birth_year |
| 9 | guest_generation |
| 10 | is_guest_youth |
| 11 | guest_birthplace |
| 12 | birthplace_region |
| 13 | region |
| 14 | federal_district |
| 15 | birthplace_country |
| 16 | guest_type |
| 17 | guest_profi |
| 18 | video_type |
| 19 | upload_date |
| 20 | upload_year |
| 21 | upload_month |
| 22 | upload_month_name |
| 23 | upload_week |
| 24 | upload_day |
| 25 | upload_weekday |
| 26 | duration |
| 27 | duration_minutes |
| 28 | english_subtitles |
| 29 | view_count |
| 30 | view_count_mln |
| 31 | like_count |
| 32 | comment_count |
| 33 | like_perc |
| 34 | comment_perc |
| 35 | comment_like_perc |
В заключение сравним количество столбцов начальных данных и дополненного датафрейма:
print(f'В начальном датафрейме : {len(df_row.axes[1])} столбцов')
print(f'В дополненном датафрейме : {len(df.axes[1])} столбцов')
print('')
print(f'Дополненный датафрейм больше первоначального на {len(df.axes[1])-len(df_row.axes[1])} столбца,\
то есть в {round(len(df.axes[1])/len(df_row.axes[1]), 2)} раза')
В начальном датафрейме : 14 столбцов В дополненном датафрейме : 36 столбцов Дополненный датафрейм больше первоначального на 22 столбца,то есть в 2.57 раза
придуманы и добавлены новые метрики:
Со всем этим багажом мы можем приступать к анализу данных.
Общую информацию о канале мы сразу собрали при помощи API в отдельный датафрейм:
vdud_main
| title | description | view_count | subscriber_count | video_count | |
|---|---|---|---|---|---|
| 0 | вДудь | Здесь задают вопросы | 1 858 295 090 | 10 100 000 | 159 |
Попробуем разобраться, в чём секрет притягательности данного канала для аудитории.
Выделим топ-10 выпусков по просмотрам за всё время:
top_view = df.sort_values(by='view_count', ascending=False).head(10)
Для удобства чтения графика выберем горизонтальный вид столбчатого графика. Поскольку просмотры - это в каком-то роде метрика привлечения внимания, для визуализации используем жёлтый цвет - ведь он тоже привлекает внимание:
plt.figure(figsize=(10, 7))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
sns.barplot(data=top_view, x='view_count_mln', y='short_video_title', color='y')
plt.title('Топ-10 выпусков по количеству просмотров за всё время')
plt.xlabel('количество просмотров, млн')
plt.ylabel('')
plt.show();
Теперь посмотрим, какие выпуски собрали больше всего лайков:
top_like = df.sort_values(by='like_count', ascending=False).head(10)
Чтобы показать, что этот параметр нас особо интересует, и чтобы подчеркнуть его "живую" природу, используем не ровный цвет, а палитру с мягким переходом оттенков:
plt.figure(figsize=(10, 7))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
palette = sns.set_palette('dark:salmon_r', 18)
sns.barplot(data=top_like, x='like_count', y='short_video_title', palette=palette)
plt.title('Топ-10 выпусков по количеству лайков за всё время')
plt.xlabel('количество лайков')
plt.ylabel('')
plt.show();
Топ-10 по лайкам отличается. Причём, можно сказать, значительно. Посмотрим, а что входит в топ-10 выпусков по количеству комментариев:
top_comment = df.sort_values(by='comment_count', ascending=False).head(10)
Для этого графика выберем контрастный тёмный цвет, например, зелёный:
plt.figure(figsize=(10, 7))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
sns.barplot(data=top_comment, x='comment_count', y='short_video_title', color='#4a6741')
plt.title('Топ-10 выпусков по количеству комментариев за всё время')
plt.xlabel('количество комментариев')
plt.ylabel('')
plt.show();
И снова у нас новый рейтинг. Сравним эти графики между собой, для удобства расположим их рядом:
plt.figure(figsize=(20, 14))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Топ-10 выпусков за всё время', fontweight='bold')
# первый график
ax1 = plt.subplot(2, 2, 1)
sns.barplot(data=top_view, x='view_count_mln', y='short_video_title', color='y')
plt.title("по количеству " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров, млн')
plt.ylabel('')
# второй график
ax2 = plt.subplot(2, 2, 2)
palette = sns.set_palette('dark:salmon_r', 18)
sns.barplot(data=top_like, x='like_count', y='short_video_title', palette=palette)
plt.title("по количеству " + r"$\bf{" + 'лайков' + "}$")
plt.xlabel('количество лайков')
plt.ylabel('')
# третий график
ax3 = plt.subplot(2, 2, 3)
sns.barplot(data=top_comment, x='comment_count', y='short_video_title', color='#4a6741')
plt.title("по количеству " + r"$\bf{" + 'комментариев' + "}$")
plt.xlabel('количество комментариев')
plt.ylabel('')
plt.tight_layout()
plt.show();
Если мы сравним топ-10 выпусков по просмотрам, лайкам и комментариям, то мы увидим смесь разных видео, где часть названий повторяется, часть не повторяется, и всё это происходит с практически постоянной перестановкой мест.
О чём это нам говорит?
Что всё это в сумме может сообщить нам?
Более подробно мы можем рассмотреть такие эффекты с помощью новых метрик.
Отбираем нужные данные:
top_like_perc = df.sort_values(by='like_perc', ascending=False).head(10)
top_comment_perc = df.sort_values(by='comment_perc', ascending=False).head(10)
top_comment_like_perc = df.sort_values(by='comment_like_perc', ascending=False).head(10)
Строим графики сразу рядом для удобства сравнения:
plt.figure(figsize=(20, 14))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Топ-10 выпусков за всё время', fontweight='bold')
# первый график
ax1 = plt.subplot(2, 2, 1)
palette = sns.color_palette('dark:salmon_r', 18)
sns.barplot(data=top_like_perc, x='like_perc', y='short_video_title', palette=palette)
plt.title("самые " + r"$\bf{" + 'понравившиеся' + "}$" + " выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
# второй график
ax2 = plt.subplot(2, 2, 2)
sns.barplot(data=top_comment_perc, x='comment_perc', y='short_video_title', color='#4a6741')
plt.title("самые " + r"$\bf{" + 'комментируемые' + "}$" + " выпуски")
plt.xlabel('% комментариев от просмотров')
plt.ylabel('')
# третий график
ax3 = plt.subplot(2, 2, 3)
sns.barplot(data=top_comment_like_perc, x='comment_like_perc', y='short_video_title', color='grey')
plt.title("выпуски, вызвавшие наибольший \n " + r"$\bf{" + 'ажиотаж/негатив' + "}$")
plt.xlabel('% комментариев от лайков')
plt.ylabel('')
plt.tight_layout()
plt.show();
Эти метрики показывают нам более скрытые и интересные проявления реакции аудитории, чем мы видели в топах на предыдущем шаге. При этом, например, процент лайков от просмотров даёт нам даже более ценную информацию, чем просто лайки или просмотры, взятые по отдельности. Итак, что мы здесь можем увидеть:
Подойдём теперь к рассмотрению показателей с противоположной стороны. Выделим новые датафреймы:
min_view = df.sort_values(by='view_count').head(10)
min_like = df.sort_values(by='like_count').head(10)
min_comment = df.sort_values(by='comment_count').head(10)
Построим графики снова рядом:
plt.figure(figsize=(20, 14))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Топ-10 выпусков по минимальному:', fontweight='bold')
# первый график
ax1 = plt.subplot(2, 2, 1)
sns.barplot(data=min_view, x='view_count_mln', y='short_video_title', color='y')
plt.title("количеству " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров, млн')
plt.ylabel('')
# второй график
ax2 = plt.subplot(2, 2, 2)
palette = sns.set_palette('dark:salmon', 14)
sns.barplot(data=min_like, x='like_count', y='short_video_title', palette=palette)
plt.title("количеству " + r"$\bf{" + 'лайков' + "}$")
plt.xlabel('количество лайков')
plt.ylabel('')
# третий график
ax3 = plt.subplot(2, 2, 3)
sns.barplot(data=min_comment, x='comment_count', y='short_video_title', color='#4a6741')
plt.title("количеству " + r"$\bf{" + 'комментариев' + "}$")
plt.xlabel('количество комментариев')
plt.ylabel('')
plt.tight_layout()
plt.show();
Создадим новые датафреймы:
min_like_perc = df.sort_values(by='like_perc').head(10)
min_comment_perc = df.sort_values(by='comment_perc').head(10)
min_comment_like_perc = df.sort_values(by='comment_like_perc').head(10)
Снова сделаем несколько графиков рядом:
plt.figure(figsize=(20, 14))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Топ-10 выпусков по минимальным показателям', fontweight='bold')
# первый график
ax1 = plt.subplot(2, 2, 1)
palette = sns.set_palette('dark:salmon', 14)
sns.barplot(data=min_like_perc, x='like_perc', y='short_video_title', palette=palette)
plt.title("минимально " + r"$\bf{" + 'понравившиеся' + "}$" + " выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
# второй график
ax2 = plt.subplot(2, 2, 2)
sns.barplot(data=min_comment_perc, x='comment_perc', y='short_video_title', color='#4a6741')
plt.title("минимально " + r"$\bf{" + 'комментируемые' + "}$" + " выпуски")
plt.xlabel('% комментариев от просмотров')
plt.ylabel('')
# третий график
ax3 = plt.subplot(2, 2, 3)
sns.barplot(data=min_comment_like_perc, x='comment_like_perc', y='short_video_title', color='grey')
plt.title("выпуски, вызвавшие наименьший \n " + r"$\bf{" + 'ажиотаж/негатив' + "}$")
plt.xlabel('% комментариев от лайков')
plt.ylabel('')
plt.tight_layout()
plt.show();
Рассчитаем средние показатели для всего датафрейма. Сравним среднее арифметическое и медиану по просмотрам:
print(f'Среднее по просмотрам равно: {round(df["view_count_mln"].mean(),2)} млн')
print(f'Медиана по просмотрам равна: {round(df["view_count_mln"].median(),2)} млн')
Среднее по просмотрам равно: 11.69 млн Медиана по просмотрам равна: 9.22 млн
Поскольку они значительно отличаются, для дальнейшего исследования будем брать медиану. Поступим аналогично и с другими параметрами.
view_median = df['view_count'].median()
like_median = df['like_count'].median()
comment_median = df['comment_count'].median()
print(f'Медианное значение просмотров: {round(view_median)}')
print(f'Медианное значение лайков: {round(like_median)}')
print(f'Медианное значение комментариев: {round(comment_median)}')
Медианное значение просмотров: 9221750 Медианное значение лайков: 246229 Медианное значение комментариев: 30274
like_perc_median = df['like_perc'].median()
comment_perc_median = df['comment_perc'].median()
comment_like_perc_median = df['comment_like_perc'].median()
print(f'Медианное значение процента лайков: {round(like_perc_median,2)}%')
print(f'Медианное значение процента комментариев: {round(comment_perc_median,2)}%')
print(f'Медианное значение процента комментариев от лайков: {round(comment_like_perc_median,2)}%')
Медианное значение процента лайков: 2.68% Медианное значение процента комментариев: 0.32% Медианное значение процента комментариев от лайков: 11.26%
Выделим выпуски, число просмотров которых заметно превысило число подписчиков канала на данный момент. Предположим, что в том случае, когда выпуск посмотрело в 2 раза больше людей по сравнению с числом подписчиков, это видео вышло вовне основной аудитории и вызвало более широкий общественный резонанс.
Для начала выделим число подпичиков:
subscriber_count = vdud_main.iloc[0]['subscriber_count']
subscriber_count
10100000
Теперь отберём в датафрейм нужные строки:
df_resonance = df.query('view_count > (@subscriber_count * 2)')
print(f'Количество выпусков, затронувших более широкую аудиторию: {len(df_resonance)}')
print(f'Доля резонансных выпусков от общего числа: {(len(df_resonance) / len(df)):.2%}')
Количество выпусков, затронувших более широкую аудиторию: 22 Доля резонансных выпусков от общего числа: 13.84%
Сначала сравним эти резонансные выпуски по основным показателям с медианным значением.
Мы отобрали эти выпуски по (рекордному) количеству просмотров, поэтому мы изначально понимаем, что все они точно превосходят медианное значение по просмотрам. Но так ли дело обстоит и с другими основными счётчиками?
Расположим строки по количеству просмотров, и у нас получится расширенный топ-10:
plt.figure(figsize=(20, 12))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски, затронувшие широкую аудиторию', fontweight='bold')
data=df_resonance.sort_values(by='view_count', ascending=False)
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=data, x='view_count', y='short_video_title', color='y')
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='lower right')
plt.title('просмотры', fontweight='bold')
plt.xlabel('количество просмотров')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
palette = sns.set_palette('dark:salmon_r', 32)
sns.barplot(data=data, x='like_count', y='short_video_title', palette=palette)
plt.axvline(x=like_median, color='black', linestyle='--', label='median')
plt.legend(loc='lower right')
plt.title('лайки', fontweight='bold')
plt.xlabel('количество лайков')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=data, x='comment_count', y='short_video_title', color='#4a6741')
plt.axvline(x=comment_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title('комментарии', fontweight='bold')
plt.xlabel('количество комментариев')
plt.ylabel('')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
Это было вполне ожидаемо: много просмотров, много и лайков с комментариями. Теперь посмотрим, влияет ли рекордное количество просмотров на показатели новых метрик: так же ли они высоки, все ли обходят медиану?
plt.figure(figsize=(20, 14))
sns.set(font_scale=1.9)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски, затронувшие широкую аудиторию', fontweight='bold')
data=df_resonance.sort_values(by='view_count', ascending=False)
# первый график
ax1 = plt.subplot(1, 3, 1)
palette = sns.set_palette('dark:salmon_r', 32)
sns.barplot(data=data, x='like_perc', y='short_video_title', palette=palette)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.title('показатель одобрения', fontweight='bold')
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
plt.legend()
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=data, x='comment_perc', y='short_video_title', color='#4a6741')
plt.axvline(x=comment_perc_median, color='black', linestyle='--', label='median')
plt.title('показатель обсуждения', fontweight='bold')
plt.xlabel('% комментариев от просмотров')
plt.ylabel('')
plt.legend()
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=data, x='comment_like_perc', y='short_video_title', color='grey')
plt.axvline(x=comment_like_perc_median, color='black', linestyle='--', label='median')
plt.title('показатель ажиотажа/негатива', fontweight='bold')
plt.xlabel('% комментариев от лайков')
plt.ylabel('')
plt.legend(loc='upper right')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
Самое время узнать, где же могут прятаться настоящие секреты успеха, какие параметры влияют на тёплый приём аудитории и обсуждаемость выпусков.
Посмотрим, гости из каких сфер участвуют в выпусках и кого из них больше.
Для построения графика с разными категориальными значениями нам понадобится палитра с разными цветами. Например, выберем такую:
sns.palplot(sns.color_palette("gist_earth_r", 20))
Нам нужно 14 цветов, уберём из этих 20 первые 2 цвета (слишком бледные) и последние 4 (слишком тёмные). Выведем на экран значения цветов, скопируем их и создадим свою палитру, убрав лишнее:
print(sns.color_palette("gist_earth_r", 20).as_hex())
['#f0dad7', '#e2c1b3', '#d5b194', '#c7a676', '#bea863', '#b9b35f', '#aab35c', '#96ae58', '#82a954', '#6ba44f', '#4e9c47', '#419552', '#3c8e61', '#368770', '#30817e', '#286d7d', '#20597b', '#184179', '#102777', '#080b75']
plt.figure(figsize=(10, 8))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
data=df.groupby('guest_profi', as_index=False).agg({'video_id':'count'}).sort_values(by='video_id', ascending=False)
my_palette=['#d5b194', '#c7a676', '#bea863', '#b9b35f', '#aab35c', '#96ae58', '#82a954',
'#6ba44f', '#4e9c47', '#419552', '#3c8e61', '#368770', '#30817e', '#286d7d']
sns.barplot(data=data, x='video_id', y='guest_profi', palette=my_palette)
plt.title('Количество выпусков с гостями из разных сфер')
plt.xlabel('количество выпусков')
plt.ylabel('')
plt.show();
Посмотрим, как реагирует аудитория на гостей из разных сфер. Например, собирают ли выпуски с музыкантами больше просмотров и лайков, чем гости из других сфер? Преобладание музыкантов - это прихоть авторов или оно подогревается желанием аудитории видеть именно таких гостей?
Создадим новый датасет, где рассчитаем средние показатели по всем сферам:
df_profession = (df.groupby('guest_profi', as_index=False)
.agg({'video_id':'count','view_count':'median','like_count':'median','comment_count':'median',
'like_perc':'median','comment_perc':'median','comment_like_perc':'median'})
.sort_values(by='video_id', ascending=False)
.reset_index(drop=True))
df_profession
| guest_profi | video_id | view_count | like_count | comment_count | like_perc | comment_perc | comment_like_perc | |
|---|---|---|---|---|---|---|---|---|
| 0 | Музыка | 42 | 7864518.0 | 224013.0 | 27308.0 | 2.931141 | 0.344583 | 11.074087 |
| 1 | Журналистика/СМИ | 22 | 8226406.0 | 196685.0 | 36523.0 | 2.383842 | 0.319685 | 15.010832 |
| 2 | Театр/кино (артисты) | 16 | 11166929.5 | 270212.0 | 29901.0 | 2.594605 | 0.266297 | 10.340905 |
| 3 | Юмор | 16 | 11724611.0 | 291913.0 | 31245.0 | 2.815510 | 0.246182 | 8.968375 |
| 4 | Театр/кино (режиссёры) | 14 | 5411870.0 | 139776.5 | 15129.0 | 2.469895 | 0.280017 | 10.788511 |
| 5 | Человек и общество | 13 | 12295892.0 | 516952.0 | 48959.0 | 4.091724 | 0.441994 | 10.586656 |
| 6 | Блогинг | 7 | 18545559.0 | 496854.0 | 38449.0 | 2.679100 | 0.254206 | 10.242057 |
| 7 | Политика | 6 | 13556029.0 | 318335.5 | 55435.5 | 2.541676 | 0.421885 | 17.352289 |
| 8 | Артисты/художники | 5 | 6535053.0 | 204716.0 | 15817.0 | 2.869831 | 0.259101 | 8.433709 |
| 9 | Бизнес | 5 | 11854295.0 | 261567.0 | 29498.0 | 2.206517 | 0.248838 | 11.963972 |
| 10 | Писатели | 4 | 6855702.0 | 231170.5 | 30909.0 | 3.207486 | 0.425666 | 12.102407 |
| 11 | Продюсеры | 4 | 9329624.5 | 196409.5 | 32401.5 | 2.102654 | 0.296338 | 16.194175 |
| 12 | Учёные | 3 | 12680234.0 | 411319.0 | 59414.0 | 3.243781 | 0.468556 | 12.882393 |
| 13 | IT-шники | 2 | 29418671.5 | 583320.5 | 51266.0 | 1.975982 | 0.197689 | 10.014125 |
Визуализируем данные, расположив профессиональные сферы так же, как в прошлом графике, от большего числа выпусков к меньшему:
plt.figure(figsize=(20, 10))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с гостями из разных профессиональных сфер', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_profession, x='view_count', y='guest_profi', palette=my_palette)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$" + " \n в каждой сфере")
plt.xlabel('количество просмотров')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_profession, x='like_count', y='guest_profi', palette=my_palette)
plt.axvline(x=like_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("среднее количество " + r"$\bf{" + 'лайков' + "}$" + " \n в каждой сфере")
plt.xlabel('количество лайков')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_profession, x='comment_count', y='guest_profi', palette=my_palette)
plt.axvline(x=comment_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("среднее количество " + r"$\bf{" + 'комментариев' + "}$" + " \n в каждой сфере")
plt.xlabel('количество комментариев')
plt.ylabel('')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
О чём это говорит?
Более точно определить отношение аудитории мы сможем с помощью новых метрик. Как же принимает аудитория самые многочисленные выпуски с гостями из сферы "музыка"? Можно предположить, что, если показатели двух положительных метрик (первых двух, а в особенности % лайков от просмотров) не выше медианы, то авторы канала сняли так много музыкантов в каком-то смысле в ущерб себе, и интересы авторов и отношение зрителей немного разошлись.
plt.figure(figsize=(20, 10))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с гостями из разных профессиональных сфер', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_profession, x='like_perc', y='guest_profi', palette=my_palette)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски \n в каждой сфере")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_profession, x='comment_perc', y='guest_profi', palette=my_palette)
plt.axvline(x=comment_perc_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("насколько " + r"$\bf{" + 'обсуждаемые' + "}$" + " выпуски \n в каждой сфере")
plt.xlabel('% комментариев от просмотров')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_profession, x='comment_like_perc', y='guest_profi', palette=my_palette)
plt.axvline(x=comment_like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='lower right')
plt.title("насколько вызвали " + r"$\bf{" + 'ажиотаж/негатив' + "}$" + " \n выпуски в каждой сфере")
plt.xlabel('% комментариев от лайков')
plt.ylabel('')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
В заключение сравним ещё раз три самых важных графика по этому вопросу:
plt.figure(figsize=(20, 10))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с гостями из разных профессиональных сфер', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_profession, x='video_id', y='guest_profi', palette=my_palette)
plt.title(r"$\bf{" + 'количество' + "}$" + " выпусков")
plt.xlabel('количество выпусков')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_profession, x='view_count', y='guest_profi', palette=my_palette)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_profession, x='like_perc', y='guest_profi', palette=my_palette)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
Сначала рассмотрим вопрос с точки зрения того, является ли гость представителем молодёжи или нет (напомню, мы имеем ввиду законодательное определение молодёжи - это люди в возрасте от 14 до 35 лет):
plt.figure(figsize=(7, 7))
palette_color = ['#d1ae8c', '#bfa764', '#b5b65e']
df_youth = (df.groupby('is_guest_youth', as_index=False)
.agg({'video_id':'count'})
.sort_values(by='is_guest_youth', ascending=False))
plt.pie(df_youth['video_id'], labels=df_youth['is_guest_youth'], autopct='%.0f%%', colors=palette_color,
counterclock=False, startangle=90, explode=(0.05,0,0))
plt.title('Соотношение молодёжи и не-молодёжи среди гостей канала')
plt.show();
Теперь рассмотрим вопрос с точки зрения поколений.
Создаём датасет с нужными данными:
df_generation = (df.groupby('guest_generation', as_index=False)
.agg({'video_id':'count','view_count':'median','like_count':'median','comment_count':'median',
'like_perc':'median','comment_perc':'median','comment_like_perc':'median'})
.sort_values(by='video_id', ascending=False)
.reset_index(drop=True))
df_generation
| guest_generation | video_id | view_count | like_count | comment_count | like_perc | comment_perc | comment_like_perc | |
|---|---|---|---|---|---|---|---|---|
| 0 | Поколение Миллениума 1982-2000 | 57 | 9208329.0 | 259682.0 | 32644.0 | 2.768500 | 0.299902 | 10.404081 |
| 1 | Поколение X 1963-1981 | 54 | 8959654.0 | 201392.5 | 23579.0 | 2.417876 | 0.281602 | 11.660247 |
| 2 | поколение неизвестно | 28 | 8434852.0 | 297592.0 | 34589.0 | 3.597032 | 0.402069 | 9.960934 |
| 3 | Бэби-бумер 1943-1962 | 18 | 9683676.5 | 184219.5 | 35757.5 | 1.909543 | 0.415701 | 17.352289 |
| 4 | Молчаливое поколение 1928-1942 | 2 | 10283242.5 | 243077.5 | 26242.0 | 2.551259 | 0.241016 | 9.674667 |
Строим график:
plt.figure(figsize=(7, 7))
palette_color = ['#d7b298', '#c4a46f', '#bbaf61', '#aab35c', '#8eac56']
plt.pie(df_generation['video_id'], labels=df_generation['guest_generation'], autopct='%.0f%%', colors=palette_color,
counterclock=False, startangle=90)
plt.title('Соотношение видео с гостями разных поколений')
plt.show();
Кстати, посмотрим, герои каких выпусков являются представителями Молчаливого поколения:
df.query('guest_generation == "Молчаливое поколение 1928-1942"')
| video_id | video_title | short_video_title | guest_name | gender | double | guest_birthday | guest_age_on_video | guest_birth_year | guest_generation | ... | duration | duration_minutes | english_subtitles | view_count | view_count_mln | like_count | comment_count | like_perc | comment_perc | comment_like_perc | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 33 | wg-TMymYSwE | Познер – цензура, страх и Путин /Vladimir Pozn... | Познер – цензура, страх и Путин... | Владимир Познер | man | no_double | 1934-04-01 | 83.0 | 1934.0 | Молчаливое поколение 1928-1942 | ... | 01:18:04 | 78 | no | 16 856 467 | 16.856467 | 380 624 | 44 365 | 2.258030 | 0.263193 | 11.655860 |
| 72 | 6oGpWlfYtRM | Олег Табаков: как он воспитывал свободных люде... | Олег Табаков: как он воспитывал... | Олег Табаков | man | no_double | 1935-08-17 | 83.0 | 1935.0 | Молчаливое поколение 1928-1942 | ... | 01:19:59 | 79 | no | 3 710 018 | 3.710018 | 105 531 | 8 119 | 2.844488 | 0.218840 | 7.693474 |
2 rows × 36 columns
Теперь посмотрим, как аудитория реагирует на гостей разных возрастов. Рассмотрим вопрос сразу с точки зрения различных поколений, т.к. это будет более информативно:
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с представителями разных поколений', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_generation, x='view_count', y='guest_generation', palette=palette_color)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$" + " \n для гостей каждого поколения")
plt.xlabel('количество просмотров')
plt.ylabel('')
plt.xlim(0, 13500000)
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_generation, x='like_count', y='guest_generation', palette=palette_color)
plt.axvline(x=like_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'лайков' + "}$" + " \n для гостей каждого поколения")
plt.xlabel('количество лайков')
plt.ylabel('')
plt.xlim(0, 410000)
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_generation, x='comment_count', y='guest_generation', palette=palette_color)
plt.axvline(x=comment_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'комментариев' + "}$" + " \n для гостей каждого поколения")
plt.xlabel('количество комментариев')
plt.ylabel('')
plt.xlim(0, 50000)
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
Складывается впечатление, что смотрят - всех, любят - молодых, комментируют - людей более зрелых. Проверим это на новых метриках:
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с представителями разных поколений', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_generation, x='like_perc', y='guest_generation', palette=palette_color)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " \n выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
plt.xlim(0, 4.5)
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_generation, x='comment_perc', y='guest_generation', palette=palette_color)
plt.axvline(x=comment_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько выпуски \n " + r"$\bf{" + 'обсуждаемые' + "}$")
plt.xlabel('% комментариев от просмотров')
plt.ylabel('')
plt.xlim(0, 0.5)
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_generation, x='comment_like_perc', y='guest_generation', palette=palette_color)
plt.axvline(x=comment_like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько вызвали \n " + r"$\bf{" + 'ажиотаж/негатив' + "}$")
plt.xlabel('% комментариев от лайков')
plt.ylabel('')
plt.xlim(0, 20)
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
Резюмируем наблюдения:
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с представителями разных поколений', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_generation, x='video_id', y='guest_generation', palette=palette_color)
plt.title(r"$\bf{" + 'количество' + "}$" + " выпусков")
plt.xlabel('количество выпусков')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_generation, x='view_count', y='guest_generation', palette=palette_color)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_generation, x='like_perc', y='guest_generation', palette=palette_color)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='lower right')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски")
plt.xlabel('процент лайков от просмотров')
plt.ylabel('')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
чем моложе поколение, тем больше выпусков с гостями такого возраста
имеются два явных лидера по количеству выпусков: поколение Миллениума и поколение Х (у каждого более 50 выпусков), а о представителях Молчаливого поколения всего 2 выпуска
эффект выборки сказывается на среднем количестве просмотров: старшие поколения заходят за медианное количество просмотров, причём 2 выпуска о людях 1934-1935 годов рождения являются лидерами. А вот лидеры по количеству выпусков не могут похвастаться здесь успехами: значит, ряд не самых просматриваемых видео были сняты как раз с ними. Получается, зрители интересуются старшим поколением и дают им аванс, который не всегда удаётся оправдать (конвертировать в высокий уровень одобрения)
представители только одного поколения смогли по-настоящему растопить зрительское сердце: и это миллениалы. Их средний процент лайков превышает среднее значение
Создадим датафрейм:
df_gender = (df.groupby('gender', as_index=False)
.agg({'video_id':'count','view_count':'median','like_count':'median','comment_count':'median',
'like_perc':'median','comment_perc':'median','comment_like_perc':'median'})
.sort_values(by='video_id', ascending=False)
.reset_index(drop=True))
df_gender
| gender | video_id | view_count | like_count | comment_count | like_perc | comment_perc | comment_like_perc | |
|---|---|---|---|---|---|---|---|---|
| 0 | man | 135 | 9221750.0 | 240757.0 | 28876.0 | 2.577775 | 0.299902 | 11.168256 |
| 1 | woman | 10 | 11212447.0 | 257000.0 | 41848.5 | 2.716710 | 0.400858 | 15.441608 |
| 2 | mixed_group | 7 | 8081739.0 | 306955.0 | 37832.0 | 3.699815 | 0.359491 | 10.438228 |
| 3 | no_gender | 7 | 12878907.0 | 521430.0 | 55202.0 | 4.048713 | 0.441994 | 10.586656 |
Выберем цвета для этого вопроса:
sns.palplot(sns.color_palette("gist_earth_r"))
print(sns.color_palette("gist_earth_r").as_hex())
['#d5b194', '#b9b35f', '#82a954', '#419552', '#30817e', '#184179']
plt.figure(figsize=(7, 7))
sns.set(font_scale=1.5)
palette_color = ['#419552','#d5b194', '#b9b35f', '#82a954']
plt.pie(df_gender['video_id'], labels=['man', 'woman', 'mixed_group', '-'], autopct='%.0f%%', colors=palette_color,
counterclock=False, startangle=90, pctdistance=0.75)
plt.title('Соотношение разных полов среди гостей канала')
plt.show();
Как мы видим, подавляющее большинство гостей - мужчины. Оправданный ли это выбор с точки зрения реакции аудитории?
plt.figure(figsize=(20, 9))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с представителями разных полов', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_gender, x='view_count', y='gender', palette=palette_color)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$" + " \n для гостей каждого пола")
plt.xlabel('количество просмотров')
plt.ylabel('')
plt.xlim(0, 13500000)
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_gender, x='like_count', y='gender', palette=palette_color)
plt.axvline(x=like_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'лайков' + "}$" + " \n для гостей каждого пола")
plt.xlabel('количество лайков')
plt.ylabel('')
plt.xlim(0, 550000)
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_gender, x='comment_count', y='gender', palette=palette_color)
plt.axvline(x=comment_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'комментариев' + "}$" + " \n для гостей каждого пола")
plt.xlabel('количество комментариев')
plt.ylabel('')
plt.xlim(0, 60000)
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
хотя выпусков с мужчинами подавляющее большинство, аудитория проявляет больший интерес к фильмам (no_gender) и интервью с женщинами
фильмы нравятся публике гораздо сильнее, чем интервью с мужчинами. И аудитория обсуждает такие выпуски активнее всего
также публика более активно реагирует на смешанные группы и интервью с женщинами (здесь возможен эффект малой выборки, но из тех данных, что есть, можно предположить и наличие тенденции: женщины и группы интересны зрителю)
Посмотрим на ситуацию с помощью новых метрик:
plt.figure(figsize=(20, 9))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с представителями разных полов', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_gender, x='like_perc', y='gender', palette=palette_color)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_gender, x='comment_perc', y='gender', palette=palette_color)
plt.axvline(x=comment_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько выпуски " + r"$\bf{" + 'обсуждаемые' + "}$")
plt.xlabel('% комментариев от просмотров')
plt.ylabel('')
plt.xlim(0, 0.45)
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_gender, x='comment_like_perc', y='gender', palette=palette_color)
plt.axvline(x=comment_like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько вызвали " + r"$\bf{" + 'ажиотаж/негатив' + "}$")
plt.xlabel('% комментариев от лайков')
plt.ylabel('')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
похоже, выпусков с мужчинами так много, что в разрезе новых метрик это сыграло злую шутку: ни по одной метрике интервью с мужчинами не дотягивают до медианы
зато мы можем сравнить остальные варианты между собой и прийти к интересному заключению: похоже, успех измеряется не полом, а тем, группа это людей или нет. Посмотрим на первый график: две категории явно вырываются вперёд - это смешанные группы и фильмы (no_gender), их публика принимает значительно теплее.
косвенно эту идею подтверждает третий график: женщины, похоже, могут вызывать негативную реакцию, а вот коллективные выпуски - нет.
Подведём итоги:
plt.figure(figsize=(20, 9))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с представителями разных полов', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_gender, x='video_id', y='gender', palette=palette_color)
plt.title(r"$\bf{" + 'количество' + "}$" + " выпусков")
plt.xlabel('количество выпусков')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_gender, x='view_count', y='gender', palette=palette_color)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
plt.xlim(0, 14000000)
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_gender, x='like_perc', y='gender', palette=palette_color)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
plt.xlim(0, 4.5)
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
в подавляющем большинстве случаев гости канала - мужчины
аудитория проявляет повышенный интерес к фильмам и интервью с женщинами
теплее всего аудитория реагирует на фильмы и смешанные группы
Получается интересный момент: канал, который известен благодаря интервью, в плане отклика аудитории имеет более тёплый приём не индивидуальных, а коллективных выпусков.
Ответ на вопрос "Уступают ли женщины мужчинам в популярности?" будет таким: "Нет, не уступают. Несмотря на небольшое число выпусков с женщинами, аудитория проявляет к ним повышенный интерес и даже чуть более тепло на них реагирует".
Рассмотрим теперь такой вопрос: из каких регионов (или стран) чаще всего оказываются родом герои интервью? Есть ли тут какая-либо закономерность? Можно предположить две противоположные версии:
print(f'Количество различных мест рождения героев выпусков: {len(df["guest_birthplace"].value_counts())}')
print(f'Количество различных регионов РФ: {len(df["birthplace_region"].value_counts())}')
print(f'Количество различных федеральных округов РФ: {len(df["federal_district"].value_counts())}')
print(f'Количество различных стран: {len(df["birthplace_country"].value_counts())}')
Количество различных мест рождения героев выпусков: 73 Количество различных регионов РФ: 33 Количество различных федеральных округов РФ: 9 Количество различных стран: 12
plt.figure(figsize=(7, 7))
palette_color = sns.color_palette(my_palette, 15)
df_region = (df.groupby('region', as_index=False)
.agg({'video_id':'count'})
.sort_values(by='region', ascending=False))
plt.pie(df_region['video_id'], labels=df_region['region'], autopct='%.0f%%', colors=palette_color,
counterclock=False, startangle=90)
plt.title('Доля регионов \n среди мест рождения в РФ')
plt.show();
как мы видим, большинство гостей, рождённых на территории современной РФ, всё-таки выходцы из различных регионов.
с другой стороны, два крупнейших города не так сильно отстают от всех регионов гигансткой по территории страны. Это заставляет задуматься.
df_region = (df.groupby('region', as_index=False)
.agg({'video_id':'count','view_count':'median','like_count':'median','comment_count':'median',
'like_perc':'median','comment_perc':'median','comment_like_perc':'median'})
.sort_values(by='region', ascending=False)
.reset_index(drop=True))
df_region
| region | video_id | view_count | like_count | comment_count | like_perc | comment_perc | comment_like_perc | |
|---|---|---|---|---|---|---|---|---|
| 0 | регионы РФ | 56 | 8509212.5 | 224013.0 | 29190.0 | 2.662011 | 0.318674 | 11.278905 |
| 1 | неизвестно | 56 | 9457309.0 | 275797.0 | 33354.0 | 3.205250 | 0.351984 | 10.799936 |
| 2 | Санкт-Петербург | 14 | 8555815.0 | 268480.0 | 19824.0 | 2.261755 | 0.244054 | 10.405974 |
| 3 | Москва | 33 | 10008304.0 | 240757.0 | 27675.0 | 2.622952 | 0.321168 | 12.523653 |
plt.figure(figsize=(20, 9))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
palette_color = sns.color_palette('gist_earth_r', 15)
plt.suptitle('Выпуски с гостями из разных мест рождения', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_region, x='video_id', y='region', palette=palette_color)
plt.title(r"$\bf{" + 'количество' + "}$" + " выпусков")
plt.xlabel('количество выпусков')
plt.ylabel('')
plt.xlim(0, 70)
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_region, x='view_count', y='region', palette=palette_color)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='lower right')
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
plt.xlim(0, 14000000)
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_region, x='like_perc', y='region', palette=palette_color)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='lower right')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
plt.xlim(0, 4)
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
большинство гостей канала, родившиеся в РФ, являются выходцами из различных регионов
больший интерес аудитория проявляет к гостям, которые родились в Москве
зрители примерно одинаково реагируют на гостей, рожденных в регионах и в Москве, но чуть теплее относятся к выходцам из регионов
Теперь посмотрим, из каких именно регионов родом гости канала. Создаём датафрейм:
df_region_name = (df.groupby('birthplace_region', as_index=False)
.agg({'video_id':'count','view_count':'median','like_count':'median','comment_count':'median',
'like_perc':'median','comment_perc':'median','comment_like_perc':'median'})
.sort_values(by='video_id', ascending=False)
.reset_index(drop=True))
df_region_name.head()
| birthplace_region | video_id | view_count | like_count | comment_count | like_perc | comment_perc | comment_like_perc | |
|---|---|---|---|---|---|---|---|---|
| 0 | неизвестно | 56 | 9457309.0 | 275797.0 | 33354.0 | 3.205250 | 0.351984 | 10.799936 |
| 1 | город Москва | 33 | 10008304.0 | 240757.0 | 27675.0 | 2.622952 | 0.321168 | 12.523653 |
| 2 | город Санкт-Петербург | 14 | 8555815.0 | 268480.0 | 19824.0 | 2.261755 | 0.244054 | 10.405974 |
| 3 | Свердловская область | 5 | 5898563.0 | 147793.0 | 19365.0 | 2.505576 | 0.359553 | 13.771753 |
| 4 | Ростовская область | 5 | 7522925.0 | 199556.0 | 14991.0 | 2.576077 | 0.254050 | 10.223553 |
Строим графики:
plt.figure(figsize=(20, 12))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с гостями, родившимися в разных регионах РФ', fontweight='bold')
palette_color = ['#e2c1b3', '#dab7a0', '#d4b091', '#cdaa82', '#c6a574', '#c0a565', '#bdaa63', '#bbaf61', '#b8b45f',
'#b0b55d', '#a7b25b', '#9db059', '#93ad57', '#87aa55', '#7da853', '#72a551', '#64a24d', '#559e49',
'#479a47', '#43974f', '#409456', '#3d905f', '#3a8c66', '#37896d', '#348575', '#31817d', '#2d7a7f',
'#29707e', '#25677d', '#215a7b', '#1d4f7a', '#194379', '#153778']
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_region_name, x='video_id', y='birthplace_region', palette=palette_color)
plt.title(r"$\bf{" + 'количество' + "}$" + " выпусков")
plt.xlabel('количество выпусков')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_region_name, x='view_count', y='birthplace_region', palette=palette_color)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_region_name, x='like_perc', y='birthplace_region', palette=palette_color)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
plt.xlim(0, 4.5)
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
несмотря на то, что подавляющее большинство гостей из тех, что родились на территории современной РФ, родились в Москве и Санкт-Петербурге, мы видим длинный список из разных регионов РФ. Из этого можно сделать вывод, что география рождения гостей выпусков обширна
можем выделить несколько областей, которые повторяются: например, по 5 героев родились в Свердловской, Ростовской, Московской областях; ещё есть области с 4 повторами, 3 и 2. То есть, несмотря на обширную географию, у регионов есть лишь небольшое количество повторений (не более 5)
на малой выборке мы видим заметные колебания (2-й и 3-й графики)
df_federal = (df.groupby('federal_district', as_index=False)
.agg({'video_id':'count','view_count':'median','like_count':'median','comment_count':'median',
'like_perc':'median','comment_perc':'median','comment_like_perc':'median'})
.sort_values(by='video_id', ascending=False)
.reset_index(drop=True))
df_federal
| federal_district | video_id | view_count | like_count | comment_count | like_perc | comment_perc | comment_like_perc | |
|---|---|---|---|---|---|---|---|---|
| 0 | неизвестно | 56 | 9457309.0 | 275797.0 | 33354.0 | 3.205250 | 0.351984 | 10.799936 |
| 1 | Центральный федеральный округ | 47 | 9461144.0 | 236515.0 | 28882.0 | 2.630452 | 0.321168 | 12.209789 |
| 2 | Северо-Западный федеральный округ | 18 | 8885895.5 | 268480.0 | 20619.0 | 2.261755 | 0.244054 | 10.405974 |
| 3 | Сибирский федеральный округ | 9 | 10834463.0 | 204907.0 | 29498.0 | 2.437887 | 0.372962 | 11.277416 |
| 4 | Уральский федеральный округ | 9 | 6249436.0 | 147793.0 | 18317.0 | 2.412227 | 0.338781 | 13.292742 |
| 5 | Приволжский федеральный округ | 8 | 14334721.0 | 434820.0 | 49451.0 | 2.716920 | 0.331127 | 11.071926 |
| 6 | Южный федеральный округ | 5 | 7522925.0 | 199556.0 | 14991.0 | 2.576077 | 0.254050 | 10.223553 |
| 7 | Дальневосточный федеральный округ | 4 | 13054712.0 | 369005.5 | 40258.0 | 3.238695 | 0.350315 | 10.429281 |
| 8 | Северо–Кавказский федеральный округ | 3 | 10609515.0 | 255984.0 | 28876.0 | 2.509964 | 0.272171 | 11.280393 |
plt.figure(figsize=(20, 9))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с гостями, родившимися в разных федеральных округах РФ', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_federal, x='video_id', y='federal_district', palette=my_palette)
plt.title(r"$\bf{" + 'количество' + "}$" + " выпусков")
plt.xlabel('количество выпусков')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_federal, x='view_count', y='federal_district', palette=my_palette)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
plt.xlim(0, 15000000)
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_federal, x='like_perc', y='federal_district', palette=my_palette)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
plt.xlim(0, 4.5)
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
большинство гостей, родившихся на территории современной РФ, выходцы из Центрального федерального округа
аудитория проявляет повышенный интерес к выпускам с гостями, рожденными в Приволжском, Дальневосточном, Сибирском и Северо-кавказском округах
теплее всего зрители реагируют на уроженцев Дальневосточного федерального округа
Посмотрим ради интереса на Приволжский и Дальневосточный округ, они выделяются на двух последних графиках:
df.query('federal_district == "Приволжский федеральный округ"')['short_video_title']
12 BadComedian - о Бондарчуке, Саше Грей... 32 Face - почему от него фанатеет... 62 Лимонов - смерть, Навальный, устрицы... 72 Олег Табаков: как он воспитывал... 85 Алексей Иванов - о сытой Москве и... 86 Егор Крид - уход из Black Star и... 117 MORGENSHTERN – главный шоумен... 145 Fасе – многое изменилось / вДудь Name: short_video_title, dtype: object
df.query('federal_district == "Дальневосточный федеральный округ"')['short_video_title']
24 Гнойный - большое интервью после... 36 Шевчук - о батле с Путиным и войне в... 107 Камчатка – полуостров, про который... 132 Орлов – комедия русской хтони / The... Name: short_video_title, dtype: object
df_country = (df.groupby('birthplace_country', as_index=False)
.agg({'video_id':'count','view_count':'median','like_count':'median','comment_count':'median',
'like_perc':'median','comment_perc':'median','comment_like_perc':'median'})
.sort_values(by='video_id', ascending=False)
.reset_index(drop=True))
df_country.head()
| birthplace_country | video_id | view_count | like_count | comment_count | like_perc | comment_perc | comment_like_perc | |
|---|---|---|---|---|---|---|---|---|
| 0 | Россия | 105 | 9199051.0 | 234370.0 | 27841.0 | 2.576077 | 0.299902 | 11.280393 |
| 1 | неизвестно | 26 | 9112732.0 | 285290.0 | 34925.0 | 3.487073 | 0.364246 | 9.731165 |
| 2 | Казахстан | 8 | 11027295.5 | 312013.5 | 36009.5 | 2.602312 | 0.335778 | 13.434211 |
| 3 | Беларусь | 6 | 9006815.5 | 285889.0 | 35532.0 | 3.209812 | 0.334155 | 12.883344 |
| 4 | Украина | 6 | 8329635.0 | 244630.0 | 36720.5 | 2.551513 | 0.362830 | 13.671477 |
plt.figure(figsize=(20, 9))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с гостями, родившимися в современных разных странах', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 3, 1)
sns.barplot(data=df_country, x='video_id', y='birthplace_country', palette=my_palette)
plt.title(r"$\bf{" + 'количество' + "}$" + " выпусков")
plt.xlabel('количество выпусков')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 3, 2)
sns.barplot(data=df_country, x='view_count', y='birthplace_country', palette=my_palette)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 3, 3)
sns.barplot(data=df_country, x='like_perc', y='birthplace_country', palette=my_palette)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
plt.xlim(0, 4)
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
plt.tight_layout()
plt.show();
подавляющее большинство гостей канала - уроженцы современной РФ, но также есть герои, которые родились в странах СНГ и единичные случаи среди тех, кто родились в более дальнем зарубежье
на малой выборке мы видим большие перепады: например уроженцы Грузии и Франции очень заинтересовали аудиторию, а уроженец США - нет
ниболее тёплый приём зрители оказали гостям, которые родились в: Грузии, США, Беларуси, Латвии, Азербайджане. Прохладно публика отреагировала на уроженцев Армении, Чехии, Франции
Конечно, всё это не очень серёзно: ведь здесь очень маленькие выборки и очень отдалённый от сути параметр, чтобы искать настоящую взаимосвязь, но ради интереса мы можем посмотреть, а кто же эти люди и что это за выпуски:
Заинтересовали аудиторию:
df.query('birthplace_country == "Грузия"')['short_video_title']
143 Акунин – что происходит с Россией /... Name: short_video_title, dtype: object
df.query('birthplace_country == "Франция"')['short_video_title']
33 Познер – цензура, страх и Путин... Name: short_video_title, dtype: object
Не заинтересовал:
df.query('birthplace_country == "США"')['short_video_title']
69 Чача - Санта-Барбара, Россия, Америка... Name: short_video_title, dtype: object
Публика прохладно приняла:
df.query('birthplace_country == "Армения"')['short_video_title']
27 Мартиросян - о рэпе, Хованском и... Name: short_video_title, dtype: object
df.query('birthplace_country == "Чехия"')['short_video_title']
63 Белый - сроки за мемы, Версус,... Name: short_video_title, dtype: object
Резюмируем наши наблюдения. Поскольку предполагать, что место рождения героя всерьёз может оказывать влияние на интерес публики или теплоту её приёма мы не будем, сосредоточимся на распределении мест рождения гостей выпусков.
plt.figure(figsize=(20, 15))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Количество выпусков с гостями, родившимися в разных:', fontweight='bold')
# первый график
ax1 = plt.subplot(2, 2, 1)
sns.barplot(data=df_country, x='video_id', y='birthplace_country', palette=my_palette)
plt.title('странах')
plt.xlabel('количество выпусков')
plt.ylabel('')
# второй график
ax2 = plt.subplot(2, 2, 2)
sns.barplot(data=df_federal, x='video_id', y='federal_district', palette=my_palette)
plt.title('федеральных округах РФ')
plt.xlabel('количество выпусков')
plt.ylabel('')
# третий график
ax3 = plt.subplot(2, 2, 3)
sns.barplot(data=df_region_name.query('video_id >1'), x='video_id', y='birthplace_region', palette=my_palette)
plt.title('регионах РФ\n (на графике только те регионы, где родились двое и более гостей)')
plt.xlabel('количество выпусков')
plt.ylabel('')
# четвёртый график
ax4 = plt.subplot(2, 2, 4)
palette_color = sns.color_palette('gist_earth_r', 15)
plt.pie(df_region['video_id'], labels=df_region['region'], autopct='%.0f%%', colors=palette_color,
counterclock=False, startangle=90)
plt.title('Доля регионов \n среди мест рождения в РФ')
plt.tight_layout()
plt.show();
герои выпусков родились в самых разных местах: на территории современной РФ, стран СНГ и даже в некоторых странах дальнего зарубежья
более 100 выпусков приходится на долю уроженцев нынешней РФ, и это подавляющее большинство
8 федеральных округов нынешней РФ стали местами рождения гостей канала. Из них чаще всего повторяются: Центральный, Северо-Западный федеральные округа
кроме Москвы и Петербурга есть ряд регионов, в которых родились двое и более гостей канала. Но повторы эти немногочисленны: не более 5 гостей родились в одном регионе. То есть вне двух самых крупных городов РФ места рождения героев словно размазаны широким слоем по всей территории страны
Существенная часть гостей выпусков родилась в Москве (21%), много гостей родилось в Санкт-Петербурге (9%), но в сумме в регионах родилось большая часть гостей (35%). Такая же доля гостей (35%) родилась не на территории современной РФ (если точнее, это и гости интервью, и фильмы, где невозможно указать место рождения).
Чтобы разобраться в этом вопросе, создадим датафрейм:
df_video_type = (df.groupby('video_type', as_index=False)
.agg({'video_id':'count','view_count':'median','like_count':'median','comment_count':'median',
'like_perc':'median','comment_perc':'median','comment_like_perc':'median'})
.sort_values(by='video_id', ascending=False)
.reset_index(drop=True))
df_video_type
| video_type | video_id | view_count | like_count | comment_count | like_perc | comment_perc | comment_like_perc | |
|---|---|---|---|---|---|---|---|---|
| 0 | Интервью | 134 | 9215039.5 | 238739.5 | 30618.5 | 2.576926 | 0.312789 | 11.824035 |
| 1 | Фильм | 25 | 10286162.0 | 288229.0 | 24211.0 | 3.575710 | 0.362442 | 9.282852 |
Посмотрим на соотношение интервью и фильмов на канале:
plt.figure(figsize=(7, 7))
palette_color = ['#d9b59d', '#bcac62']
plt.pie(df_video_type['video_id'], labels=df_video_type['video_type'], autopct='%.0f%%', colors=palette_color,
counterclock=False, startangle=90)
plt.title('Соотношение интервью и фильмов \n среди всех выпусков')
plt.show();
Как мы видим, подавляющее большинство выпусков - это интервью. Посмотрим, оправдан ли этот выбор с точки зрения зрительской реакции? Так ли любят именно этот жанр?
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски в разных жанрах: в чём отличие', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 5, 1)
sns.barplot(data=df_video_type, x='video_id', y='video_type', palette=palette_color)
plt.title(r"$\bf{" + 'количество' + "}$" + " \n выпусков")
plt.xlabel('количество выпусков')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 5, 2)
sns.barplot(data=df_video_type, x='view_count', y='video_type', palette=palette_color)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("среднее количество \n" + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 5, 3)
sns.barplot(data=df_video_type, x='like_perc', y='video_type', palette=palette_color)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " \n выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
# четвёртый график
ax4 = plt.subplot(1, 5, 4)
sns.barplot(data=df_video_type, x='comment_perc', y='video_type', palette=palette_color)
plt.axvline(x=comment_perc_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("насколько выпуски \n" + r"$\bf{" + 'обсуждаемые' + "}$")
plt.xlabel('% комментариев от просмотров')
plt.ylabel('')
ax4.set(yticklabels=[])
ax4.tick_params(left=False)
# пятый график
ax5 = plt.subplot(1, 5, 5)
sns.barplot(data=df_video_type, x='comment_like_perc', y='video_type', palette=palette_color)
plt.axvline(x=comment_like_perc_median, color='black', linestyle='--', label='median')
plt.legend()
plt.title("насколько вызвали \n" + r"$\bf{" + 'ажиотаж/негатив' + "}$")
plt.xlabel('% комментариев от лайков')
plt.ylabel('')
ax5.set(yticklabels=[])
ax5.tick_params(left=False)
plt.tight_layout()
plt.show();
несмотря на заметный отрыв интервью по количеству выпусков, фильмы опережают этот жанр по всем "позитивным" метрикам: фильмами больше интересуются, гораздо лучше оценивают, больше комментируют
интервью опережает по метрике ажиотажа/негатива
За последнее время обнаружилось новое свойство канала: наблюдение за изменениями героев. Ряд гостей спустя годы снова пришли на интервью, а один герой "продублировался" сразу в двух выпусках, выпущенных подряд. Сравним выпуски с "дублями" гостей. Собираем нужные данные:
df_double = (df.groupby('double', as_index=False)
.agg({'video_id':'count'})
.sort_values(by='video_id', ascending=False))
df_double_true = (df.query('double == "double"')
.sort_values(by='guest_name'))
Посмотрим, сколько всего таких "дублированных" выпусков в соотношении со всеми выпусками:
plt.figure(figsize=(7, 7))
plt.pie(df_double['video_id'], labels=df_double['double'], autopct='%.0f%%', colors=palette_color,
counterclock=False, startangle=90, explode=(0, 0.05))
plt.title('Доля повторных интервью среди всех выпусков')
plt.show();
plt.figure(figsize=(20, 9))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с гостями, пришедшими на интервью во второй раз', fontweight='bold')
palette_color = ['#276b7d','#276b7d','#3c8f60','#3c8f60','#72a551','#72a551',
'#b5b65e','#b5b65e','#d1ae8c', '#d1ae8c']
# первый график
ax1 = plt.subplot(1, 4, 1)
sns.barplot(data=df_double_true, x='view_count', y='short_video_title', palette=palette_color)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("количество \n" + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 4, 2)
sns.barplot(data=df_double_true, x='like_perc', y='short_video_title', palette=palette_color)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " \n выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 4, 3)
sns.barplot(data=df_double_true, x='comment_perc', y='short_video_title', palette=palette_color)
plt.axvline(x=comment_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько выпуски \n " + r"$\bf{" + 'обсуждаемые' + "}$")
plt.xlabel('% комментариев от просмотров')
plt.ylabel('')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
# четвёртый график
ax4 = plt.subplot(1, 4, 4)
sns.barplot(data=df_double_true, x='comment_like_perc', y='short_video_title', palette=palette_color)
plt.axvline(x=comment_like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='upper right')
plt.title("насколько вызвали \n " + r"$\bf{" + 'ажиотаж/негатив' + "}$")
plt.xlabel('% комментариев от лайков')
plt.ylabel('')
ax4.set(yticklabels=[])
ax4.tick_params(left=False)
plt.tight_layout()
plt.show();
С какого-то времени на канале стали появляться английские субтитры. Посмотрим, сколько таких выпусков и влияет ли в среднем наличие субтитров на просмотры и на реакцию на видео.
Посмотрим, кстати, когда вышло первое видео с такими субтитрами:
df.query('english_subtitles == "yes"')['upload_date'].min()
Timestamp('2017-04-18 00:00:00')
Первое видео датируется 2017 годом, получается, это выпуск с первого года существования канала.
Теперь создадим датафрейм с необходимыми данными:
df_subtitle = (df.groupby('english_subtitles', as_index=False)
.agg({'video_id':'count','view_count':'median','like_count':'median','comment_count':'median',
'like_perc':'median','comment_perc':'median','comment_like_perc':'median'})
.sort_values(by='video_id', ascending=False)
.reset_index(drop=True))
Посмотрим, сколько таких выпусков среди общего количества видео на канале:
plt.figure(figsize=(7, 7))
palette_color = ['#d9b59d', '#bcac62']
plt.pie(df_subtitle['video_id'], labels=df_subtitle['english_subtitles'], autopct='%.0f%%', colors=palette_color,
counterclock=False, startangle=90, explode=(0, 0.05))
plt.title('Доля выпусков с английскими субтитрами')
plt.show();
И теперь оценим общую картину:
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Выпуски с субтитрами на английском языке (yes) и без них (no)', fontweight='bold')
# первый график
ax1 = plt.subplot(1, 5, 1)
sns.barplot(data=df_subtitle, x='video_id', y='english_subtitles', palette=palette_color)
plt.title(r"$\bf{" + 'количество' + "}$" + " выпусков")
plt.xlabel('количество выпусков')
plt.ylabel('')
# второй график
ax2 = plt.subplot(1, 5, 2)
sns.barplot(data=df_subtitle, x='view_count', y='english_subtitles', palette=palette_color)
plt.axvline(x=view_median, color='black', linestyle='--', label='median')
plt.legend(loc='best')
plt.title("среднее количество \n " + r"$\bf{" + 'просмотров' + "}$")
plt.xlabel('количество просмотров')
plt.ylabel('')
ax2.set(yticklabels=[])
ax2.tick_params(left=False)
# третий график
ax3 = plt.subplot(1, 5, 3)
sns.barplot(data=df_subtitle, x='like_perc', y='english_subtitles', palette=palette_color)
plt.axvline(x=like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='best')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " \n выпуски")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
ax3.set(yticklabels=[])
ax3.tick_params(left=False)
# четвёртый график
ax4 = plt.subplot(1, 5, 4)
sns.barplot(data=df_subtitle, x='comment_perc', y='english_subtitles', palette=palette_color)
plt.axvline(x=comment_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='best')
plt.title("насколько выпуски \n " + r"$\bf{" + 'обсуждаемые' + "}$")
plt.xlabel('% комментариев от просмотров')
plt.ylabel('')
ax4.set(yticklabels=[])
ax4.tick_params(left=False)
# пятый график
ax5 = plt.subplot(1, 5, 5)
sns.barplot(data=df_subtitle, x='comment_like_perc', y='english_subtitles', palette=palette_color)
plt.axvline(x=comment_like_perc_median, color='black', linestyle='--', label='median')
plt.legend(loc='best')
plt.title("насколько вызвали \n " + r"$\bf{" + 'ажиотаж/негатив' + "}$")
plt.xlabel('% комментариев от лайков')
plt.ylabel('')
ax5.set(yticklabels=[])
ax5.tick_params(left=False)
plt.tight_layout()
plt.show();
большинство выпусков - без английских субтитров
среднее количество просмотров выше у выпусков с субтитрами
видео с субтитрами и без них одинаково нравятся аудитории
выпуски с субтитрами более обсуждаемые и возможно, они вызывают ажиотаж/негатив
Итого: выпуски с субтитрами действительно больше просматриваются, активнее комментируются, но они не завоёвывают никакой особой зрительской любви.
Чтобы узнать ответ на этот впорос, последовательно пройдёмся по всем данным, что у нас есть. Начнём с дней (чисел месяца).
Создадим датафрейм:
df_day = (df.groupby(pd.Grouper(key='upload_day'), as_index=False)
.agg({'video_id':'count'})
.sort_values(by='upload_day')
.reset_index(drop=True))
Построим график:
plt.figure(figsize=(20, 8))
palette_color = ['#e6c7bc', '#e1bfb0', '#debba7', '#d9b59d', '#d5b194', '#d1ae8c', '#cdaa82', '#caa87b',
'#c5a571', '#c1a367', '#bfa664', '#bea963', '#bcac62', '#bbaf61', '#b9b360', '#b8b55f',
'#b4b65d', '#afb55c', '#a8b35b', '#a2b15a', '#9db059', '#96ae58', '#91ad57', '#8aab56',
'#84a954', '#7fa853', '#78a652', '#72a551', '#69a34e', '#60a14c', '#599f4a', '#509d48',
'#479a47', '#44994b', '#429650', '#419554', '#3f9258', '#3d905d', '#3c8e61', '#3a8c66',
'#388a6a', '#37886e', '#358673', '#338477', '#31827c', '#308080', '#2d7a7f', '#2b737e',
'#296f7e', '#26687d', '#24647c', '#215c7c', '#1f557b']
sns.barplot(data=df_day, x='upload_day', y='video_id', palette=palette_color)
plt.title('Количество выпусков, выложенных в разные числа месяца')
plt.xlabel('число месяца')
plt.ylabel('количество видео')
plt.show();
видео выкладываются во все числа месяца без исключений. Количество выпусков варьируется от 1 до 9.
Теперь посмотрим на дни недели:
df_weekday = (df.groupby(pd.Grouper(key='upload_weekday'), as_index=False)
.agg({'video_id':'count'})
.assign(day_number=(2, 1, 3, 5, 4))
.sort_values(by='day_number')
.reset_index(drop=True))
plt.figure(figsize=(10, 6))
sns.barplot(data=df_weekday, x='upload_weekday', y='video_id', palette='gist_earth_r')
plt.title('Количество выпусков, выложенных в разные дни недели')
plt.xlabel('дни недели')
plt.ylabel('количество видео')
plt.show();
вот здесь уже интересней, мы видим закономерность: выпуски выходят только в будние дни, преимущественно в середине недели, в основном по вторникам
скорее всего это уже сознательный выбор, а не случайное распределение, как в числах месяца.
Теперь рассмотрим по неделям (каждая неделя пронумерована по порядку на протяжении года):
df_week = (df.groupby(pd.Grouper(key='upload_week'), as_index=False)
.agg({'video_id':'count'})
.sort_values(by='upload_week')
.reset_index(drop=True))
plt.figure(figsize=(20, 8))
sns.barplot(data=df_week, x='upload_week', y='video_id', palette=palette_color)
plt.title('Количество выпусков, выложенных в разные недели')
plt.xlabel('номер недели')
plt.ylabel('количество видео')
plt.show();
выпуски выходят практически во все недели года, за исключением 1-й недели (когда ещё празднуется Новый год)
мы видим определённую сезонность: минимальное количество выпусков - в период праздников и отпусков (2, 3, 52, 53 недели - около Нового года, 25-28 недели - это июнь-июль, 44 неделя - около ноябрьских выходных)
в остальных случаях количество выпусков колеблется от 2 до 6
Теперь посмотрим, каково распределение по месяцам. Создадим датафрейм:
df_month = (df.groupby(pd.Grouper(key='upload_month'), as_index=False)
.agg({'video_id':'count'})
.assign(month_name= df.upload_month_name.unique())
.sort_values(by='upload_month')
.reset_index(drop=True))
plt.figure(figsize=(20, 8))
sns.barplot(data=df_month, x='month_name', y='video_id', palette='gist_earth_r')
plt.title('Количество выпусков, выложенных в разные месяцы')
plt.xlabel('месяцы')
plt.ylabel('количество видео')
plt.show();
выпуски выходят во все месяцы года
в сезонный спад это 6 выпусков за все годы (январь и июль)
максимум это в 3 раза больше - по 18 видео за все годы (май и октябрь)
И осталось посмотреть, каково распределение видео по годам:
df_year = (df.groupby(pd.Grouper(key='upload_year'), as_index=False)
.agg({'video_id':'count'})
.sort_values(by='upload_year')
.reset_index(drop=True))
plt.figure(figsize=(20, 8))
sns.barplot(data=df_year, x='upload_year', y='video_id', palette='gist_earth_r')
plt.title('Количество выпусков, выложенных в разные годы')
plt.xlabel('годы')
plt.ylabel('количество видео')
plt.show();
Что интересного мы нашли:
Попробуем визуализировать периодичность выхода выпусков. Для этого создадим ряд датафреймов, где будут данные о номерах недель и количестве видео за каждый год:
df_week_2017 = (df.loc[df['upload_year'] == 2017]
.groupby(pd.Grouper(key='upload_week'), as_index=False)
.agg({'video_id':'count'})
.sort_values(by='upload_week')
.reset_index(drop=True))
df_week_2018 = (df.loc[df['upload_year'] == 2018]
.groupby(pd.Grouper(key='upload_week'), as_index=False)
.agg({'video_id':'count'})
.sort_values(by='upload_week')
.reset_index(drop=True))
df_week_2019 = (df.loc[df['upload_year'] == 2019]
.groupby(pd.Grouper(key='upload_week'), as_index=False)
.agg({'video_id':'count'})
.sort_values(by='upload_week')
.reset_index(drop=True))
df_week_2020 = (df.loc[df['upload_year'] == 2020]
.groupby(pd.Grouper(key='upload_week'), as_index=False)
.agg({'video_id':'count'})
.sort_values(by='upload_week')
.reset_index(drop=True))
df_week_2021 = (df.loc[df['upload_year'] == 2021]
.groupby(pd.Grouper(key='upload_week'), as_index=False)
.agg({'video_id':'count'})
.sort_values(by='upload_week')
.reset_index(drop=True))
df_week_2022 = (df.loc[df['upload_year'] == 2022]
.groupby(pd.Grouper(key='upload_week'), as_index=False)
.agg({'video_id':'count'})
.sort_values(by='upload_week')
.reset_index(drop=True))
df_week_2017.head()
| upload_week | video_id | |
|---|---|---|
| 0 | 6 | 1 |
| 1 | 7 | 1 |
| 2 | 8 | 1 |
| 3 | 10 | 1 |
| 4 | 11 | 1 |
Поскольку нам нужны все недели, а не только те, в которые выходили видео, создадим список во всеми неделями в году от 1 до 53 и объединим эти данные с имеющимися датафреймами по каждому году. Там, где нет видео, будет 0. Таким образом мы сможем увидеть промежутки между выходом видео за каждый год.
upload_week=list(range(1, 54))
all_weeks = pd.DataFrame(upload_week)
all_weeks.columns=['upload_week']
df_week_2017 = df_week_2017.merge(all_weeks, on='upload_week', how='right').fillna(0)
df_week_2018 = df_week_2018.merge(all_weeks, on='upload_week', how='right').fillna(0)
df_week_2019 = df_week_2019.merge(all_weeks, on='upload_week', how='right').fillna(0)
df_week_2020 = df_week_2020.merge(all_weeks, on='upload_week', how='right').fillna(0)
df_week_2021 = df_week_2021.merge(all_weeks, on='upload_week', how='right').fillna(0)
df_week_2022 = df_week_2022.merge(all_weeks, on='upload_week', how='right').fillna(0)
Посмотрим на совместный график:
plt.figure(figsize=(20, 12))
ax1=plt.subplot(6, 1, 1)
sns.barplot(data=df_week_2017, x='upload_week', y='video_id', palette=palette_color)
number = 2017
plt.title("Количество выпусков, выложенных в разные недели " + r"$\bf{" + str(number) + "}$" + " года")
plt.xlabel('')
plt.ylabel('кол-во \nвидео')
ax2=plt.subplot(6, 1, 2)
sns.barplot(data=df_week_2018, x='upload_week', y='video_id', palette=palette_color)
number = 2018
plt.title("Количество выпусков, выложенных в разные недели " + r"$\bf{" + str(number) + "}$" + " года")
plt.xlabel('')
plt.ylabel('кол-во \nвидео')
ax3=plt.subplot(6, 1, 3)
sns.barplot(data=df_week_2019, x='upload_week', y='video_id', palette=palette_color)
number = 2019
plt.title("Количество выпусков, выложенных в разные недели " + r"$\bf{" + str(number) + "}$" + " года")
plt.xlabel('')
plt.ylabel('кол-во \nвидео')
ax4=plt.subplot(6, 1, 4)
sns.barplot(data=df_week_2020, x='upload_week', y='video_id', palette=palette_color)
number = 2020
plt.title("Количество выпусков, выложенных в разные недели " + r"$\bf{" + str(number) + "}$" + " года")
plt.xlabel('')
plt.ylabel('кол-во \nвидео')
ax5=plt.subplot(6, 1, 5)
sns.barplot(data=df_week_2021, x='upload_week', y='video_id', palette=palette_color)
number = 2021
plt.title("Количество выпусков, выложенных в разные недели " + r"$\bf{" + str(number) + "}$" + " года")
plt.xlabel('')
plt.ylabel('кол-во \nвидео')
ax6=plt.subplot(6, 1, 6)
sns.barplot(data=df_week_2022, x='upload_week', y='video_id', palette=palette_color)
number = 2022
plt.title("Количество выпусков, выложенных в разные недели " + r"$\bf{" + str(number) + "}$" + " года")
plt.xlabel('')
plt.ylabel('кол-во \nвидео')
plt.tight_layout()
plt.show();
Таким образом мы видим, что периодичность выхода видео на канале с 2017 по 2022 год сильно изменилась. Можно выделить два больших этапа:
Посмотрим, что происходило с продолжительностью видео в разные годы. Создадим датафрейм:
df_length = (df.groupby('upload_year', as_index=False)
.agg({'duration_minutes':['median','sum'], 'video_id':'count'}))
df_length.columns=['upload_year','duration_median', 'duration_sum', 'video_count']
df_length['duration_sum']=df_length['duration_sum']/60
df_length
| upload_year | duration_median | duration_sum | video_count | |
|---|---|---|---|---|
| 0 | 2 017 | 58.0 | 38.666667 | 40 |
| 1 | 2 018 | 71.0 | 44.866667 | 35 |
| 2 | 2 019 | 85.5 | 37.850000 | 24 |
| 3 | 2 020 | 102.0 | 41.900000 | 23 |
| 4 | 2 021 | 113.0 | 34.966667 | 19 |
| 5 | 2 022 | 116.0 | 36.716667 | 18 |
Посмотрим на графике:
plt.figure(figsize=(20, 15))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Изменение продолжительности видео по годам', fontweight='bold')
ax1=plt.subplot(2, 2, 1)
sns.barplot(data=df_length, x='upload_year', y='duration_median', palette='gist_earth_r')
plt.title(r"$\bf{" + 'средняя' + "}$" + " продолжительность")
plt.xlabel('годы')
plt.ylabel('продолжительность в минутах')
ax2=plt.subplot(2, 2, 2)
sns.barplot(data=df_length, x='upload_year', y='duration_sum', palette='gist_earth_r', ax=ax2)
sns.lineplot(data=df_length['video_count'], palette='gist_earth_r', marker='o', ax=ax2,
label='количество выпусков')
plt.title(r"$\bf{" + 'суммарная' + "}$" + " продолжительность")
plt.xlabel('годы')
plt.ylabel('продолжительность в часах')
plt.tight_layout()
plt.show();
По годам рост продолжительности видео происходил плавно. Но так ли это на самом деле? Увеличивалась ли длина всех видео постепенно или это связано с жанром фильма и его более высокой продолжительностью?
plt.figure(figsize=(20, 15))
palette_color = ['#d9b59d', '#bcac62']
df_genre = (df.groupby(['upload_year', 'video_type'], as_index=False)
.agg({'video_id':'count', 'duration_minutes':'median'}))
ax1 = plt.subplot(2, 2, 1)
sns.barplot(data=df_genre, x='upload_year', y='duration_minutes', hue='video_type', palette=palette_color)
plt.title('средняя ' r"$\bf{" + 'продолжительность' + "}$" + ' выпусков в минутах'
'\n в разные годы с разделением по жанрам')
plt.xlabel('годы')
plt.ylabel('продолжительность в минутах')
ax2 = plt.subplot(2, 2, 2)
sns.barplot(data=df_genre, x='upload_year', y='video_id', hue='video_type', palette=palette_color)
plt.title(r"$\bf{" + 'количество' + "}$" + ' выпусков в разные годы \n с разделением по жанрам')
plt.xlabel('годы')
plt.ylabel('количество видео')
plt.tight_layout()
plt.show();
Вывод: длина выпусков увеличивалась год от года независимо от жанра. Причём если вначале жанр фильма был движущей силой этой тенденции, то в 2021 году интервью обогнали фильмы по средней длине.
Можно предположить две версии:
Посмотрим, какая версия ближе к реальности.
Начнём с определения границы, что мы будем считать коротким и длинным видео. Построим диаграмму размаха:
plt.figure(figsize=(10, 6))
sns.boxplot(y=df['duration_minutes'], x=df['upload_year'], palette='gist_earth_r')
plt.title('Диаграмма размаха: продолжительность видео \n в разные годы')
plt.xlabel('годы выхода видео')
plt.ylabel('продолжительность в минутах')
plt.show();
Если мы установим планку разделения на категории "короткое видео/длинное видео", например, на уровне 80 минут, то получится, что мы по большому счёту просто будем сравнивать видео первых лет ("коротких видео") с выпусками последних лет ("длинными видео"). Это может привести нас к неверным выводам: ведь число подписчиков росло с каждым годом, соответсвенно это могло оказывать влияние на все показатели (независимо от длины видео). Лучше попробуем отталкиваться в измерениях от медианы каждого года: больше медианы - "длинное", меньше медианы - "короткое" видео.
Проведём подготовительные расчёты:
df_median = (df.groupby('upload_year', as_index=False)
.agg({'duration_minutes':'median'}))
median_by_years = df_median['duration_minutes']
Поделим видео на короткие и длинные по медиане каждого года:
df_2017_short = df.query('upload_year ==2017 and duration_minutes < @median_by_years[0]')
df_2017_long = df.query('upload_year ==2017 and duration_minutes >= @median_by_years[0]')
df_2018_short = df.query('upload_year ==2018 and duration_minutes < @median_by_years[1]')
df_2018_long = df.query('upload_year ==2018 and duration_minutes >= @median_by_years[1]')
df_2019_short = df.query('upload_year ==2019 and duration_minutes < @median_by_years[2]')
df_2019_long = df.query('upload_year ==2019 and duration_minutes >= @median_by_years[2]')
df_2020_short = df.query('upload_year ==2020 and duration_minutes < @median_by_years[3]')
df_2020_long = df.query('upload_year ==2020 and duration_minutes >= @median_by_years[3]')
df_2021_short = df.query('upload_year ==2021 and duration_minutes < @median_by_years[4]')
df_2021_long = df.query('upload_year ==2021 and duration_minutes >= @median_by_years[4]')
df_2022_short = df.query('upload_year ==2022 and duration_minutes < @median_by_years[5]')
df_2022_long = df.query('upload_year ==2022 and duration_minutes >= @median_by_years[5]')
Сцепим полученные датафреймы за каждый год в два больших: короткие и длинные видео.
df_short = pd.concat([df_2017_short, df_2018_short, df_2019_short, df_2020_short, df_2021_short, df_2022_short],
ignore_index=True)
df_long = pd.concat([df_2017_long, df_2018_long, df_2019_long, df_2020_long, df_2021_long, df_2022_long],
ignore_index=True)
Посчитаем метрики зрительской реакции: возьмём просмотры, также узнаем, насколько зрителям понравились видео, насколько они стали обсуждаемыми и вызвали ли ажиотаж/негатив.
df_short_grouped = (df_short.groupby('upload_year', as_index=False)
.agg({'view_count':'median', 'like_perc':'median',
'comment_perc':'median','comment_like_perc':'median'}))
df_long_grouped = (df_long.groupby('upload_year', as_index=False)
.agg({'view_count':'median', 'like_perc':'median',
'comment_perc':'median','comment_like_perc':'median'}))
Визуализируем данные:
plt.figure(figsize=(20, 12))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Реакция аудитории на "короткие" и "длинные" видео в разные годы', fontweight='bold')
ax1 = plt.subplot(2, 2, 1)
sns.barplot(data=df_short_grouped, x='upload_year', y='view_count', ax=ax1,
label='short', palette='gist_earth_r')
sns.lineplot(data=df_long_grouped['view_count'], marker='o', ax=ax1, label='long')
plt.title("среднее количество " + r"$\bf{" + 'просмотров' + "}$")
plt.legend()
plt.xlabel('годы выхода видео')
plt.ylabel('количество просмотров')
ax2 = plt.subplot(2, 2, 2)
sns.barplot(data=df_short_grouped, x='upload_year', y='like_perc', ax=ax2,
label='short', palette='gist_earth_r')
sns.lineplot(data=df_long_grouped['like_perc'], marker='o', ax=ax2, label='long')
plt.title("насколько " + r"$\bf{" + 'понравились' + "}$" + " выпуски")
plt.legend()
plt.xlabel('годы выхода видео')
plt.ylabel('% лайков от просмотров')
ax3 = plt.subplot(2, 2, 3)
sns.barplot(data=df_short_grouped, x='upload_year', y='comment_perc', ax=ax3,
label='short', palette='gist_earth_r')
sns.lineplot(data=df_long_grouped['comment_perc'], marker='o', ax=ax3, label='long')
plt.title("насколько выпуски " + r"$\bf{" + 'обсуждаемые' + "}$")
plt.legend()
plt.xlabel('годы выхода видео')
plt.ylabel('% комментариев от просмотров')
ax4 = plt.subplot(2, 2, 4)
sns.barplot(data=df_short_grouped, x='upload_year', y='comment_like_perc', ax=ax4,
label='short', palette='gist_earth_r')
sns.lineplot(data=df_long_grouped['comment_like_perc'], marker='o', ax=ax4, label='long')
plt.title("насколько вызвали " + r"$\bf{" + 'ажиотаж/негатив' + "}$")
plt.legend()
plt.xlabel('годы выхода видео')
plt.ylabel('% комментариев от лайков')
plt.tight_layout()
plt.show();
Итого: к длинным выпускам аудитория проявляет бОльший интерес, но теплее реагирует на короткие видео.
Кажется, 2022-1 год несёт какие-то изменения: короткие выпуски вышли на первый план по всем направлениям. Скорее всего, это связано с тем, что длина видео выросла настолько, что "короткие видео" в 2022-м году - это максимально длинные по меркам 2017 года.
Рассмотрим выпуски отдельно по каждому году. Выделим топ-10 за каждый год по двум самым показательным метрикам: количеству просмотров и проценту лайков от просмотров. То есть мы узнаем, что больше всего заинтересовало аудиторию, и что больше всего понравилось.
plt.figure(figsize=(20, 12))
plt.suptitle('Топ-10 самых просматриваемых выпусков за каждый год', fontweight='bold')
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
df_2017 = (df.query('upload_year ==2017')
.sort_values(by='view_count_mln', ascending=False)
.head(10))
ax1=plt.subplot(2, 3, 1)
sns.barplot(data=df_2017, x='view_count_mln', y='short_video_title', color='#d5b194')
number = 2017
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('просмотры, млн')
plt.ylabel('')
df_2018 = (df.query('upload_year ==2018')
.sort_values(by='view_count_mln', ascending=False)
.head(10))
ax2=plt.subplot(2, 3, 2, sharex=ax1)
sns.barplot(data=df_2018, x='view_count_mln', y='short_video_title', color='#b9b35f')
number = 2018
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('просмотры, млн')
plt.ylabel('')
df_2019 = (df.query('upload_year ==2019')
.sort_values(by='view_count_mln', ascending=False)
.head(10))
ax3=plt.subplot(2, 3, 3, sharex=ax1)
sns.barplot(data=df_2019, x='view_count_mln', y='short_video_title', color='#82a954')
number = 2019
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('просмотры, млн')
plt.ylabel('')
df_2020 = (df.query('upload_year ==2020')
.sort_values(by='view_count_mln', ascending=False)
.head(10))
ax4=plt.subplot(2, 3, 4, sharex=ax1)
sns.barplot(data=df_2020, x='view_count_mln', y='short_video_title', color='#419552')
number = 2020
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('просмотры, млн')
plt.ylabel('')
df_2021 = (df.query('upload_year ==2021')
.sort_values(by='view_count_mln', ascending=False)
.head(10))
ax5=plt.subplot(2, 3, 5, sharex=ax1)
sns.barplot(data=df_2021, x='view_count_mln', y='short_video_title', color='#30817e')
number = 2021
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('просмотры, млн')
plt.ylabel('')
df_2022 = (df.query('upload_year ==2022')
.sort_values(by='view_count_mln', ascending=False)
.head(10))
ax6=plt.subplot(2, 3, 6, sharex=ax1)
sns.barplot(data=df_2022, x='view_count_mln', y='short_video_title', color='#184179')
number = 2022
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('просмотры, млн')
plt.ylabel('')
plt.tight_layout()
plt.show();
plt.figure(figsize=(20, 14))
plt.suptitle('Топ-10 самых полюбившихся зрителям выпусков за каждый год', fontweight='bold')
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
df_2017 = (df.query('upload_year ==2017')
.sort_values(by='like_perc', ascending=False)
.head(10))
ax1=plt.subplot(2, 3, 1)
sns.barplot(data=df_2017, x='like_perc', y='short_video_title', color='#d5b194')
number = 2017
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
df_2018 = (df.query('upload_year ==2018')
.sort_values(by='like_perc', ascending=False)
.head(10))
ax2=plt.subplot(2, 3, 2, sharex=ax1)
sns.barplot(data=df_2018, x='like_perc', y='short_video_title', color='#b9b35f')
number = 2018
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
df_2019 = (df.query('upload_year ==2019')
.sort_values(by='like_perc', ascending=False)
.head(10))
ax3=plt.subplot(2, 3, 3, sharex=ax1)
sns.barplot(data=df_2019, x='like_perc', y='short_video_title', color='#82a954')
number = 2019
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
df_2020 = (df.query('upload_year ==2020')
.sort_values(by='like_perc', ascending=False)
.head(10))
ax4=plt.subplot(2, 3, 4, sharex=ax1)
sns.barplot(data=df_2020, x='like_perc', y='short_video_title', color='#419552')
number = 2020
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
df_2021 = (df.query('upload_year ==2021')
.sort_values(by='like_perc', ascending=False)
.head(10))
ax5=plt.subplot(2, 3, 5, sharex=ax1)
sns.barplot(data=df_2021, x='like_perc', y='short_video_title', color='#30817e')
number = 2021
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
df_2022 = (df.query('upload_year ==2022')
.sort_values(by='like_perc', ascending=False)
.head(10))
ax6=plt.subplot(2, 3, 6, sharex=ax1)
sns.barplot(data=df_2022, x='like_perc', y='short_video_title', color='#184179')
number = 2022
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('% лайков от просмотров')
plt.ylabel('')
plt.tight_layout()
plt.show();
Посмотрим, есть ли какая-либо тенденция в том, гостей какого пола приглашают на интервью. Например, чаще ли со временем стали приглашать женщин? Или, может быть, одинаково редко во все годы?
Агрегируем нужные данные:
df_year_gender = (df.groupby(['upload_year', 'gender'], as_index=False)
.agg({'video_id':'count'}))
df_year = (df.groupby('upload_year', as_index=False)
.agg({'video_title':'count'}))
df_year_gender= df_year_gender.merge(df_year, on='upload_year')
df_year_gender['percent'] = df_year_gender['video_id']/df_year_gender['video_title']*100
df_year_gender.head()
| upload_year | gender | video_id | video_title | percent | |
|---|---|---|---|---|---|
| 0 | 2 017 | man | 39 | 40 | 97.500000 |
| 1 | 2 017 | woman | 1 | 40 | 2.500000 |
| 2 | 2 018 | man | 31 | 35 | 88.571429 |
| 3 | 2 018 | mixed_group | 2 | 35 | 5.714286 |
| 4 | 2 018 | woman | 2 | 35 | 5.714286 |
Построим несколько графиков. Для начала узнаем:
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Гости разных полов по годам', fontweight='bold')
palette_color = ['#d5b194','#82a954', '#b9b35f','#419552']
ax1=plt.subplot(1, 2, 1)
ax=sns.histplot(df_year_gender.sort_values(by='gender', ascending=False), x='upload_year', hue='gender',
weights='video_id', discrete=True, shrink=.8, multiple="stack", alpha=1, palette=palette_color)
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title(r"$\bf{" + 'количество' + "}$")
plt.xlabel('годы выпуска видео')
plt.ylabel('количество выпусков')
ax2=plt.subplot(1, 2, 2)
ax=sns.histplot(df_year_gender.sort_values(by='gender', ascending=False), x='upload_year', hue='gender',
weights='percent', discrete=True, shrink=.8, multiple="stack", alpha=1, palette=palette_color,
legend=False)
plt.title(r"$\bf{" + 'процент' + "}$")
plt.xlabel('годы выпуска видео')
plt.ylabel('% от общего числа')
plt.tight_layout()
plt.show();
Теперь посмотрим:
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Время появления гостей разных полов и отношение к ним аудитории', fontweight='bold')
ax=sns.scatterplot(data=df, x='upload_date', y='gender',
hue="like_perc", size="view_count", sizes=(20, 200))
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title('размер - просмотры, цвет - % лайков от просмотров')
plt.xlabel('годы выпуска видео')
plt.ylabel('')
plt.show();
Теперь взглянем на возраст героев. Есть ли какая-либо тенденция: меняется ли с годами количество и соотношение гостей молодого и зрелого возраста?
Сначала рассмотрим вопрос с наболее обобщённого ракурса: явлется ли гость молодёжью (по действующему законодательству РФ это люди от 14 до 35 лет).
Выделяем нужные данные:
df_year_youth = (df.groupby(['is_guest_youth', 'upload_year'], as_index=False)
.agg({'video_id':'count'})
.merge(df_year, on='upload_year'))
df_year_youth['percent'] = round(df_year_youth['video_id']/df_year_youth['video_title']*100)
df_year_youth.head()
| is_guest_youth | upload_year | video_id | video_title | percent | |
|---|---|---|---|---|---|
| 0 | no | 2 017 | 21 | 40 | 52.0 |
| 1 | other | 2 017 | 1 | 40 | 2.0 |
| 2 | yes | 2 017 | 18 | 40 | 45.0 |
| 3 | no | 2 018 | 23 | 35 | 66.0 |
| 4 | other | 2 018 | 4 | 35 | 11.0 |
Визуализируем:
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Молодёжь и не-молодёжь в гостях у канала в разные годы', fontweight='bold')
palette_color = ['#b5b65e', '#bfa764', '#d1ae8c']
ax1=plt.subplot(1, 2, 1)
ax=sns.histplot(df_year_youth, x='upload_year', hue='is_guest_youth', palette=palette_color,
weights='video_id', discrete=True, shrink=.8, multiple="stack", alpha=1)
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title(r"$\bf{" + 'количество' + "}$")
plt.xlabel('годы выпуска видео')
plt.ylabel('количество выпусков')
ax2=plt.subplot(1, 2, 2)
ax=sns.histplot(df_year_youth, x='upload_year', hue='is_guest_youth', palette=palette_color,
weights='percent', discrete=True, shrink=.8, multiple="stack", alpha=1, legend=False)
plt.title(r"$\bf{" + 'процент' + "}$")
plt.xlabel('годы выпуска видео')
plt.ylabel('% от общего числа')
ax2.bar_label(ax2.containers[0], label_type='center', color='black')
ax2.bar_label(ax2.containers[2], label_type='center', color='black')
plt.tight_layout()
plt.show();
Теперь рассмотрим ситуацию с точки зрения поколений:
df_year_generation = (df.groupby(['upload_year', 'guest_generation'], as_index=False)
.agg({'video_id':'count'})
.sort_values(by='video_id', ascending=False)
.merge(df_year, on='upload_year'))
df_year_generation['percent'] = round(df_year_generation['video_id']/df_year_generation['video_title']*100)
df_year_generation.rename(columns={'video_id': 'video_cnt'}, inplace=True)
df_year_generation = (df_year_generation.merge(df_generation[['guest_generation', 'video_id']],
on='guest_generation', how='left')
.sort_values(by=['upload_year','video_id'], ascending=[True,False]))
df_year_generation.head()
| upload_year | guest_generation | video_cnt | video_title | percent | video_id | |
|---|---|---|---|---|---|---|
| 1 | 2 017 | Поколение Миллениума 1982-2000 | 16 | 40 | 40.0 | 57 |
| 0 | 2 017 | Поколение X 1963-1981 | 18 | 40 | 45.0 | 54 |
| 4 | 2 017 | поколение неизвестно | 1 | 40 | 2.0 | 28 |
| 2 | 2 017 | Бэби-бумер 1943-1962 | 4 | 40 | 10.0 | 18 |
| 3 | 2 017 | Молчаливое поколение 1928-1942 | 1 | 40 | 2.0 | 2 |
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
palette_color = ['#d7b298', '#c4a46f', '#bbaf61', '#aab35c', '#8eac56']
plt.suptitle('Представители разных поколений в гостях у канала по годам', fontweight='bold')
ax1=plt.subplot(1, 2, 1)
ax=sns.histplot(df_year_generation, x='upload_year', hue='guest_generation', palette=palette_color,
weights='video_cnt', discrete=True, shrink=.8, multiple="stack", alpha=1)
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title(r"$\bf{" + 'количество' + "}$")
plt.xlabel('годы выпуска видео')
plt.ylabel('количество выпусков')
ax1.bar_label(ax1.containers[4], label_type='center', color='black')
ax1.bar_label(ax1.containers[3], label_type='center', color='black')
ax2=plt.subplot(1, 2, 2)
ax=sns.histplot(df_year_generation, x='upload_year', hue='guest_generation', palette=palette_color,
weights='percent', discrete=True, shrink=.8, multiple="stack", alpha=1, legend=False)
plt.title(r"$\bf{" + 'процент' + "}$")
plt.xlabel('годы выпуска видео')
plt.ylabel('% от общего числа')
ax2.bar_label(ax2.containers[4], label_type='center', color='black')
ax2.bar_label(ax2.containers[3], label_type='center', color='black')
plt.tight_layout()
plt.show();
Но можно заметить 3 небольших наблюдения:
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Время появления гостей разных поколений и отношение к ним аудитории', fontweight='bold')
ax=sns.scatterplot(data=df, x='upload_date', y='guest_generation',
hue="like_perc", size="view_count", sizes=(20, 200))
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title('размер - просмотры, цвет - % лайков от просмотров')
plt.xlabel('годы выпуска видео')
plt.ylabel('')
plt.show();
Какие наблюдения мы имеем:
во время, кроме 2021 года, на канале было больше видео с людьми старше 35 лет, чем с молодёжью
в основном выпуски с двумя самыми старшими поколениями приходились на первые 2-3 года, затем они исчезли и Бэби-бумеры возникли вновь в 2022 (то есть без этого исключения можно было бы подумать, что интерес к самым зрелым гостям себя исчерпал)
Таким образом мы видим разнообразие в вопросе возраста. Публика хорошо реагирует на разнообразие поколений, отдавая предпочтение Миллениалам.
Рассмотрим вопрос профессий. Сможем ли мы выделить какие-то периоды, когда было много представителей одной сферы, потом другой? Или они распределялись равномерно по годам?
df_year_profi = (df.groupby(['upload_year', 'guest_profi'], as_index=False)
.agg({'video_id':'count'})
.sort_values(by='video_id', ascending=False)
.merge(df_year, on='upload_year'))
df_year_profi['percent'] = df_year_profi['video_id']/df_year_profi['video_title']*100
df_year_profi.head()
| upload_year | guest_profi | video_id | video_title | percent | |
|---|---|---|---|---|---|
| 0 | 2 017 | Музыка | 17 | 40 | 42.5 |
| 1 | 2 017 | Бизнес | 4 | 40 | 10.0 |
| 2 | 2 017 | Театр/кино (режиссёры) | 4 | 40 | 10.0 |
| 3 | 2 017 | Блогинг | 3 | 40 | 7.5 |
| 4 | 2 017 | Театр/кино (артисты) | 3 | 40 | 7.5 |
Поскольку у нас много профессий, рассмотрим ситуацию на серии графиков по годам:
plt.figure(figsize=(20, 12))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Количество гостей разных профессий по годам', fontweight='bold')
ax1=plt.subplot(2, 3, 1)
sns.barplot(data=df_year_profi.query('upload_year == 2017'),
x='video_id', y='guest_profi', palette=my_palette)
number = 2017
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('количество видео')
plt.ylabel('')
ax2=plt.subplot(2, 3, 2, sharex=ax1)
sns.barplot(data=df_year_profi.query('upload_year == 2018'),
x='video_id', y='guest_profi', palette=my_palette)
number = 2018
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('количество видео')
plt.ylabel('')
ax3=plt.subplot(2, 3, 3, sharex=ax1)
sns.barplot(data=df_year_profi.query('upload_year == 2019'),
x='video_id', y='guest_profi', palette=my_palette)
number = 2019
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('количество видео')
plt.ylabel('')
ax4=plt.subplot(2, 3, 4, sharex=ax1)
sns.barplot(data=df_year_profi.query('upload_year == 2020'),
x='video_id', y='guest_profi', palette=my_palette)
number = 2020
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('количество видео')
plt.ylabel('')
ax5=plt.subplot(2, 3, 5, sharex=ax1)
sns.barplot(data=df_year_profi.query('upload_year == 2021'),
x='video_id', y='guest_profi', palette=my_palette)
number = 2021
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('количество видео')
plt.ylabel('')
ax6=plt.subplot(2, 3, 6, sharex=ax1)
sns.barplot(data=df_year_profi.query('upload_year == 2022'),
x='video_id', y='guest_profi', palette=my_palette)
number = 2022
plt.title(r"$\bf{" + str(number) + "}$")
plt.xlabel('количество видео')
plt.ylabel('')
plt.tight_layout()
plt.show();
plt.figure(figsize=(20, 8))
sns.set(font_scale=1.7)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Время появления гостей разных профессий и отношение к ним аудитории', fontweight='bold')
ax=sns.scatterplot(data=df, x='upload_date', y='guest_profi',
hue="like_perc", size="view_count", sizes=(20, 200))
sns.move_legend(ax, "upper left", bbox_to_anchor=(1, 1))
plt.title('размер - просмотры, цвет - % лайков от просмотров')
plt.xlabel('годы выпуска видео')
plt.ylabel('')
plt.show();
Таким образом мы снова видим разнообразие, также как и в вопросе возраста. И снова аудитория положительно реагирует на такое разнообразие.
15 апреля 2022 года Минюст РФ внёс Дудя в реестр СМИ — «иностранных агентов». Посмотрим, как на это отреагировала аудитория.
Изменилось ли внимание публики (количество просмотров)? Проявилась ли в большей степени положительная (% лайков от просмотров) или ажиотажно-негативная реакция (% комментариев от лайков) аудитории?
plt.figure(figsize=(20, 12))
sns.set(font_scale=1.5)
plt.style.use('seaborn-whitegrid')
plt.suptitle('Изменение отношения аудитории к выпускам', fontweight='bold')
date_status = pd.to_datetime('2022-04-15', format='%Y-%m-%d')
ax1=plt.subplot(2, 2, 1)
sns.lineplot(data=df, x='upload_date', y='view_count_mln', color='black')
plt.axvline(x=date_status, color='black', linestyle='--', label='статус иноагента')
plt.legend()
plt.title("насколько выпуски " + r"$\bf{" + 'интересны' + "}$")
plt.xlabel('время выхода видео')
plt.ylabel('просмотры, млн')
ax2=plt.subplot(2, 2, 2)
sns.lineplot(data=df, x='upload_date', y='like_perc', color='black')
plt.axvline(x=date_status, color='black', linestyle='--', label='статус иноагента')
plt.legend()
plt.title("насколько " + r"$\bf{" + 'нравятся' + "}$" + " выпуски")
plt.xlabel('время выхода видео')
plt.ylabel('% лайков от просмотров')
ax3=plt.subplot(2, 2, 3)
sns.lineplot(data=df, x='upload_date', y='comment_like_perc', color='black')
plt.axvline(x=date_status, color='black', linestyle='--', label='статус иноагента')
plt.legend()
plt.title("насколько вызвали " + r"$\bf{" + 'ажиотаж/негатив' + "}$")
plt.xlabel('время выхода видео')
plt.ylabel('% комментариев от лайков')
plt.tight_layout()
plt.show();
Итого: канал продолжает жить своей жизнью несмотря на новый иноагентский статус автора: все показатели в среднем растут, а самые заметные рекорды и анти-рекорды остались позади.
Подводя итог, выделим универсальный рейтинг. Для этого создадим новый параметр, где рассчитаем соотношение количества просмотров и подписчиков:
df['view_subscr'] = df['view_count'] / subscriber_count
Сложим наше новое измерение с "коэффициентом одобрения" и "коэффициентом обсуждаемости" (то есть с положительными метриками) и вычтем показатель негативной реакции (разделенный на 10, чтобы порядки чисел были сопоставимы):
df['rating'] = df['view_subscr'] + df['like_perc'] + df['comment_perc'] - df['comment_like_perc']/10
Так мы получим свой вариант рейтинга, где сразу учитывается влияние и положительных, и отрицательной метрик.
df.sort_values(by='rating', ascending=False).head(10)
| video_id | video_title | short_video_title | guest_name | gender | double | guest_birthday | guest_age_on_video | guest_birth_year | guest_generation | ... | english_subtitles | view_count | view_count_mln | like_count | comment_count | like_perc | comment_perc | comment_like_perc | view_subscr | rating | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 100 | GTRAEpllGZo | ВИЧ в России / HIV in Russia (Eng Rus subtit... | ВИЧ в России / HIV in Russia (Eng Rus... | ВИЧ | no_gender | no_double | NaT | NaN | NaN | поколение неизвестно | ... | no | 23 738 856 | 23.738856 | 1 170 190 | 108 627 | 4.929429 | 0.457592 | 9.282852 | 2.350382 | 6.809117 |
| 89 | vF1UGmi5m8s | Беслан. Помни / Beslan. Remember (english es... | Беслан. Помни / Beslan. Remember... | Беслан | no_gender | no_double | NaT | NaN | NaN | поколение неизвестно | ... | no | 25 395 488 | 25.395488 | 1 184 523 | 130 463 | 4.664305 | 0.513725 | 11.013969 | 2.514405 | 6.591038 |
| 125 | e2hBvHFtzHA | Ивангай – где он пропадал / вДудь | Ивангай – где он пропадал / вДудь | Иван Рудской | man | no_double | 1996-01-19 | 25.0 | 1996.0 | Поколение Миллениума 1982-2000 | ... | no | 22 109 185 | 22.109185 | 1 030 630 | 95 251 | 4.661547 | 0.430821 | 9.242017 | 2.189028 | 6.357194 |
| 105 | 9lO06Zxhu88 | Как устроена IT-столица мира / Russian Silicon... | Как устроена IT-столица мира /... | Дороничев, Давыдов, Думик, Невгень | man | no_double | NaT | NaN | NaN | поколение неизвестно | ... | yes | 48 984 995 | 48.984995 | 972 974 | 79 585 | 1.986269 | 0.162468 | 8.179561 | 4.850000 | 6.180781 |
| 115 | vps43rXgaZc | Навальные – интервью после отравления / The Na... | Навальные – интервью после отравления... | Алексей и Юлия Навальные | mixed_group | double | 1976-06-04 | 44.0 | 1976.0 | Поколение X 1963-1981 | ... | yes | 34 557 077 | 34.557077 | 1 278 548 | 223 917 | 3.699815 | 0.647963 | 17.513382 | 3.421493 | 6.017933 |
| 139 | E_2Vy9B8hic | Почему в России пытают / Why They Torture Peop... | Почему в России пытают / Why They... | Игорь Каляпин | man | no_double | NaT | NaN | NaN | поколение неизвестно | ... | no | 12 629 227 | 12.629227 | 640 430 | 62 201 | 5.071015 | 0.492516 | 9.712381 | 1.250419 | 5.842712 |
| 121 | recHRnwvAaY | Птушкин – главный путешественник ютуба / вДудь | Птушкин – главный путешественник... | Антон Птушкин | man | no_double | 1984-05-22 | 36.0 | 1984.0 | Поколение Миллениума 1982-2000 | ... | no | 26 567 386 | 26.567386 | 908 026 | 77 408 | 3.417822 | 0.291365 | 8.524866 | 2.630434 | 5.487135 |
| 102 | Uzu9clzaLvg | Лапенко – новая звезда русского интернета (Eng... | Лапенко – новая звезда русского... | Антон Лапенко | man | no_double | 1986-09-01 | 33.0 | 1986.0 | Поколение Миллениума 1982-2000 | ... | no | 22 818 897 | 22.818897 | 853 222 | 73 287 | 3.739103 | 0.321168 | 8.589441 | 2.259297 | 5.460623 |
| 82 | oo1WouI38rQ | Колыма - родина нашего страха / Kolyma - Birth... | Колыма - родина нашего страха /... | Колыма | no_gender | no_double | NaT | NaN | NaN | поколение неизвестно | ... | no | 27 618 873 | 27.618873 | 1 049 219 | 187 941 | 3.798920 | 0.680480 | 17.912466 | 2.734542 | 5.422696 |
| 99 | x-N2PmMGfnI | Щербаков - спецназ, панк-рок, любовь (English ... | Щербаков - спецназ, панк-рок, любовь... | Алексей Щербаков | man | no_double | 1988-12-15 | 31.0 | 1988.0 | Поколение Миллениума 1982-2000 | ... | yes | 26 509 453 | 26.509453 | 925 176 | 126 438 | 3.489985 | 0.476954 | 13.666373 | 2.624698 | 5.225001 |
10 rows × 38 columns
Ещё мы можем создать тепловую карту. Поскольку у нас очень разные величины в метриках (например, просмотры - в миллионах, комментарии - в тысячах, а коээфициент одобрения, например, в единицах), мы будем отображать не сами значения метрик, а место выпуска в рейтинге по каждому параметру. Например, выпуск "Как устроена IT-столица мира" будет иметь "1" в столбце рейтинга просмотров, и "6" в рейтинге количества лайков.
Создаём таблицу с местами в рейтингах:
rating_view = (df.sort_values(by='view_count', ascending=False)
.reset_index(drop=True)['short_video_title'])
rating_view = pd.DataFrame(rating_view)
rating_number = list(range(1, 160))
rating_number = pd.DataFrame(rating_number)
rating_table = rating_view.join(rating_number)
rating_table.columns=['video', 'views']
rating_like_cnt = (df.sort_values(by='like_count', ascending=False)
.reset_index(drop=True)['short_video_title'])
rating_like_cnt = pd.DataFrame(rating_like_cnt)
rating_like_cnt = rating_like_cnt.join(rating_number)
rating_like_cnt.columns=['video', 'likes']
rating_table = rating_table.merge(rating_like_cnt, on='video', how='left')
rating_comment_cnt = (df.sort_values(by='comment_count', ascending=False)
.reset_index(drop=True)['short_video_title'])
rating_comment_cnt = pd.DataFrame(rating_comment_cnt)
rating_comment_cnt = rating_comment_cnt.join(rating_number)
rating_comment_cnt.columns=['video', 'comments']
rating_table = rating_table.merge(rating_comment_cnt, on='video', how='left')
rating_like_perc = (df.sort_values(by='like_perc', ascending=False)
.reset_index(drop=True)['short_video_title'])
rating_like_perc = pd.DataFrame(rating_like_perc)
rating_like_perc = rating_like_perc.join(rating_number)
rating_like_perc.columns=['video', 'positive']
rating_table = rating_table.merge(rating_like_perc, on='video', how='left')
rating_comment_perc = (df.sort_values(by='comment_perc', ascending=False)
.reset_index(drop=True)['short_video_title'])
rating_comment_perc = pd.DataFrame(rating_comment_perc)
rating_comment_perc = rating_comment_perc.join(rating_number)
rating_comment_perc.columns=['video', 'discuss']
rating_table = rating_table.merge(rating_comment_perc, on='video', how='left')
rating_comment_like_perc = (df.sort_values(by='comment_like_perc', ascending=False)
.reset_index(drop=True)['short_video_title'])
rating_comment_like_perc = pd.DataFrame(rating_comment_like_perc)
rating_comment_like_perc = rating_comment_like_perc.join(rating_number)
rating_comment_like_perc.columns=['video', 'hype/negative']
rating_table = rating_table.merge(rating_comment_like_perc, on='video', how='left')
rating_common = (df.sort_values(by='rating', ascending=False)
.reset_index(drop=True)['short_video_title'])
rating_common = pd.DataFrame(rating_common)
rating_common = rating_common.join(rating_number)
rating_common.columns=['video', 'rating']
rating_table = rating_table.merge(rating_common, on='video', how='left')
rating_table = rating_table.set_index('video')
rating_table
| views | likes | comments | positive | discuss | hype/negative | rating | |
|---|---|---|---|---|---|---|---|
| video | |||||||
| Как устроена IT-столица мира /... | 1 | 6 | 16 | 132 | 149 | 133 | 4 |
| Ивлеева - про Элджея, секс и пластику... | 2 | 15 | 14 | 137 | 113 | 56 | 22 |
| Нагиев - пенсии, стих в Кремле... | 3 | 14 | 24 | 126 | 139 | 115 | 16 |
| Навальные – интервью после отравления... | 4 | 1 | 1 | 22 | 8 | 24 | 5 |
| Гордон - Украина, Россия, Ukraine,... | 5 | 17 | 10 | 131 | 82 | 33 | 40 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| Кватания – из чего получаются клипы и... | 155 | 152 | 150 | 75 | 93 | 94 | 111 |
| Animal Джаz – мировой стрит-арт и... | 156 | 149 | 147 | 59 | 67 | 76 | 99 |
| Чача - Санта-Барбара, Россия, Америка... | 157 | 147 | 117 | 41 | 13 | 31 | 98 |
| Роднянский - о Бондарчуке, `Оскаре` и... | 158 | 158 | 153 | 97 | 111 | 93 | 126 |
| Хлебников - лучший русский... | 159 | 159 | 154 | 99 | 99 | 81 | 133 |
159 rows × 7 columns
plt.figure(figsize=(20, 16))
sns.set(font_scale=1.9)
plt.style.use('seaborn-whitegrid')
ax=sns.heatmap(rating_table.head(20),
annot=True, annot_kws={'fontsize': 20,'color':'black'}, fmt=".0f", linewidth=2, linecolor='black',
vmin=0, vmax=160, center= 250, cmap = 'gist_earth_r')
plt.title('Топ-20 заинтересовавших аудиторию выпусков \n (количество просмотров)',
fontsize=20, fontweight="bold", pad=20)
plt.xlabel('')
plt.ylabel('')
plt.show();
plt.figure(figsize=(20, 16))
sns.set(font_scale=1.9)
plt.style.use('seaborn-whitegrid')
ax=sns.heatmap(rating_table.sort_values(by='positive').head(20),
annot=True, annot_kws={'fontsize': 20,'color':'black'}, fmt=".0f", linewidth=2, linecolor='black',
vmin=0, vmax=160, center=250, cmap = 'gist_earth_r')
plt.title('Топ-20 самых полюбившихся аудитории выпусков \n (% лайков от просмотров)',
fontsize=20, fontweight="bold", pad=20)
plt.xlabel('')
plt.ylabel('')
plt.show();
plt.figure(figsize=(20, 16))
sns.set(font_scale=1.9)
plt.style.use('seaborn-whitegrid')
ax=sns.heatmap(rating_table.sort_values(by='rating').head(20),
annot=True, annot_kws={'fontsize': 20,'color':'black'}, fmt=".0f", linewidth=2, linecolor='black',
vmin=0, vmax=160, center=250, cmap = 'gist_earth_r')
plt.title('Топ-20 выпусков в универсальном рейтинге',
fontsize=20, fontweight="bold", pad=20)
plt.xlabel('')
plt.ylabel('')
plt.show();
Был произведён анализ одного из крупнейших русскоязычных каналов видеоплатформы youtube (Роскомнадзор: сайт нарушает закон РФ). Данные были собраны при помощи API, были произведены дополнения из других источников.
Главные цифры канала:
суммарное количество просмотров на канале составляет гигантскую цифру: это более 1 млрд 800 млн просмотров
у канала более 10 млн подписчиков
на момент исследования выложено 159 видео
Безусловный лидер по просмотрам - выпуск "Как устроена IT-столица мира". В лидерах по количеству просмотров, лайков и комментариев разные выпуски (исключение - 1-е место по лайкам и комментариям). Это говорит о том, что аудитория по-разному проявляет заинтересованность (просмотры), одобрение (лайки) и обсуждение (комментарии) к разным выпускам.
Для более детального исследования были придуманы новые метрики. Одна из них - % лайков от просмотров - показывает уровень одобрения, насколько видео понравилось пользователям. Эта метрика говорит о том, что аудитория наиболее тепло встречает документальные фильмы на сложные и даже трагические темы. Две другие новые метрики - % комментариев от просмотров и % комментариев от лайков - показывают уровень обсуждаемости и уровень ажиотажа/негатива в комментариях. Благодаря этим метрикам мы видим, что наиболее дискуссионными получаются политические и около-политические выпуски.
Также с политикой связаны выпуски с наименьшим уровнем одобрения, а выпуски, имеющие минимумы по всем остальным направлениям, зачастую просто видео первого года существования канала.
Рассчитав средние значения по всем метрикам, мы увидели, что рекордные просмотры (в 2 раза бОльшие, чем число подписчиков) приносят много лайков и комментариев (всегда выше среднего), но не в тех же пропорциях. Это значит, что успех видео измеряется не только просмотрами и каждый раз есть какой-то индивидуальный фактор. Это подтверждают и новые метрики: выпуски с рекордными просмотрами только примерно в половине случаев превышают медиану по уровням одобрения, обсуждения и ажиотажа/негатива. То есть даже рекордные просмотры не гарантируют, что выпуск станет любимчиком публики или вызовет ажиотаж.
Далее в процессе исследования были разобраны вопросы, связанные с нахождением факторов, влияющих на заинтересованность и одобрение публики. Первым был разобран вопрос профессий гостей.
Доминирующая профессиональная сфера гостей - музыка. Также много героев, чья деятельность связана с журналистикой, юмором, театром. При этом "музыка" не доходит до медианы ни по одному из основных метрик (количество просмотров, лайков, комментариев) и лишь ненамного превышает средние значения по уровню одобрения и обсуждения. У аудитории другие фавориты - больше всего пользователи ценят тему "человек и общество", а также писателей и учёных. Обсуждают больше всего эти же упомятнутые три направления плюс политику. А наибольший ажиотаж/негатив вызывают политики, продюсеры и журналисты. Резюмируя наблюдения, можно сказать: больше всего снимают - музыкантов, смотрят - IT и блогинг, любят - тему "человек и общество".
Возраст и поколения гостей. "Молодёжь" (по законодательству люди в возрасте от 14 до 35 лет) не преобладет среди гостей канала. В разрезе поколений мы видим представителей 4-х разных возрастов: от "Молчаливого поколения" (1928-1942) до "поколения Миллениума" (1982-2000). По отношению аудитории мы видим: чуть больше среднего интересуются старшими поколениями, любят - Миллениалов и комментируют Бэби-бумеров. Новые метрики добавляют: зрители больше всего любят не интервью, а выпуски-фильмы, а ещё у аудитории явно непростые отношения с представителями поколения Бэби-бума (минимум одобрения, максимум ажиотажа/негатива). Подводя итог: снимают - молодых, смотрят - зрелых, любят - Миллениалов.
Пол. Большинство гостей канала - мужчины. Но аудитория в среднем активнее реагирует на женщин и смешанные групы, чем на интервью с мужчинами. По новым метрикам мы опять видим, что в фаворитах аудитории скорее выпуски-фильмы, чем интервью. Резюмируя: большинство выпусков на канале - интервью с мужчинами, но аудитория в среднем проявляет бОльший интерес к выпускам-фильмам и интервью с женщинами, а ценит выпуски со смешанными группами и выпуски-фильмы.
Место рождения. География мест рождения гостей канала широка: это РФ, страны СНГ и дальнего зарубежья, а в составе РФ несколько федеральных округов и регионов. Большинство гостей родились на территории современной РФ, многие - в Москве. Аудитория больше интересуется гостями, рождёнными в Москве, но чуть больше любит выходцев из регионов.
Жанр выпуска. Большинство выпусков на канале - интервью. Но аудитория больше смотрит, любит и обсуждает выпуски-фильмы. Самое заметное отличие - в метриках одобрения и ажиотажа/негатива.
Повторные интервью. Небольшое число гостей появлялись на канале во второй раз. Не всегда повторные выпуски привлекали больше внимания аудитории, но каждый раз они сильнее нравились и становились более обсуждаемыми по сравнению с первыми выпусками.
Субтитры на английском языке. Небольшая доля выпусков имеет английские субтитры. Такие выпуски больше смотрят и обсуждают, но они не вызывают бОльшую симпатию, а скорее ажиотаж/негатив.
Далее данные были разобраны по вопросам, связанным со временем:
чаще всего выпуски появляются на канале по вторникам, иногда в другие будние дни, никогда - в выходные. Выпуски появляются во все месяцы, но есть сезонные спады во время каникул/отпусков.
с каждым годом выходит всё меньше выпусков, особенно заметно снижение от 2017 к 2019 году
периодичность выхода видео изменилась: от еженедельных выпусков с чёткими перерывами на отпуск к более размеренному распределению по всему году
средняя продолжительность выпусков за все годы увеличилась в 2 раза, а количество выпусков в 2 раза упало. Итог - относительный баланс суммарной продолжительности всех видео по годам
аудитория обычно больше интересуется "длинными" видео, а теплее принимает "короткие"
Ещё о тенденциях:
с годами количество и доля гостей-мужчин снижается
2022-й год изменил наметившуюся тенденцию к увеличению долю молодёжи среди гостей выпусков
с годами стало преобладать поколение Миллениалов
с годами заметный перевес в количестве гостей-музыкантов сгладился, на первый план вышли гости-журналисты
объявление иноагентом не вызвало никакого кардинального изменения в отношении аудитории, но, возможно, подорвало наметившуюся в 2020-22 годах тенденцию к ещё бОльшему увеличению уровня одобрения
наиболее заинтересовавшие широкую аудторию выпуски далеко не всегда имеют такое же высокое одобрение
и наоборот: наиболее одобряемые выпуски часто имеют низкое место в рейтинге по просмотрам
эту проблему можно устранить с помощью новых расчётов - "универсальный" рейтинг помогает сгладить эти отличия и выявить более сбалансированную картину самых значимых выпусков
P.S. Можно по-разному относиться к каналу и его автору, но надо признать: он не выходит из десятки инфлюенсеров весь прошедший год: https://romir.ru/studies/reyting-inflyuenserov-romir-blogery-ustupili-mesto-muzykantam-i-akteram. А в рейтинге инфлюенсеров-блогеров он держится и того выше: https://romir.ru/studies/romir-kseniya-sobchak-vernulas-na-1-mesto-v-reytinge-inflyuenserov-sredi-blogerov.
Кроме того, надо признать его влияние на индустрию:
А по фирменным вопросам и их эволюции от "Сколько ты зарабатываешь", "Что ты скажешь, оказавшись перед Путиным" через "сколько тебе будет в 2036 году" и к "ты допускаешь, что больше никогда не вернёшься в Россию" можно будет судить о конце 2010-х начале 2020-х годов в России.
*Роскомнадзор: сайт нарушает закон РФ
В исследовании упоминаются или могут упоминаться следующие иноагенты:
Всех этих людей Минюст признал иноагентами. При построении графиков нет возможности вносить изменения и ставить звёзочки * прямо в графики, поэтому указываю здесь. Спасибо за внимание.
Презентация: https://disk.yandex.ru/i/mqp3xOvdb0cjrA